← Назад к вопросам

Какие объекты попадают в стек

1.2 Junior🔥 221 комментариев
#JVM и память#Kotlin основы

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Работа стека вызовов в Java/Kotlin (Android)

В контексте стека вызовов (call stack) или просто стека, речь идет о структуре данных в памяти, которая управляет выполнением методов/функций в программе. Стек работает по принципу LIFO (Last In, First Out). При вызове метода в стек помещается новый кадр стека (stack frame), который содержит информацию о текущем вызове. Этот кадр удаляется при возврате из метода.

Какие объекты попадают в стек?

В стеке хранятся не сами объекты, а примитивные типы данных и ссылки на объекты, которые существуют в куче (heap). Это ключевое отличие.

Хранится в стеке:

  • Локальные переменные примитивных типов: int, long, float, double, boolean, char, byte, short.
  • Локальные переменные-ссылки (references) на объекты. Сами объекты лежат в куче.
  • Параметры методов (аргументы).
  • Адреса возврата (return addresses) — информация о том, куда вернуться после завершения метода.
  • Другие служебные данные для управления вызовом методов.

Хранится в куче (heap):

  • Все объекты (экземпляры классов), созданные с помощью оператора new.
  • Массивы (даже массивы примитивов).
  • Статические переменные (хранятся в области памяти, связанной с классом, которая также не является стеком).
  • Объекты String (хотя для них есть пул строк, но сам объект — в куче).

Пример для иллюстрации

Рассмотрим пример на Kotlin:

class User(val name: String, val age: Int)

fun calculateScore(base: Int): Int { // Параметр `base` — в стеке
    val multiplier = 2               // Примитивная `multiplier` — в стеке
    val result = base * multiplier   // Примитивная `result` — в стеке
    return result
}

fun main() {
    val primitiveValue = 10                // Примитив — в стеке
    val user: User = User("Alice", 30)     // Ссылка `user` — в стеке, сам объект User — в куче
    val score = calculateScore(primitiveValue) // Вызов метода создает новый кадр в стеке

    println("User: ${user.name}, Score: $score")
}

Что происходит в памяти:

  1. При запуске main() создается кадр стека для этого метода.
  2. В кадр main() помещается переменная primitiveValue (значение 10).
  3. Оператор User("Alice", 30) создает объект в куче. В кадр стека main() помещается только ссылка user на этот объект.
  4. При вызове calculateScore() создается новый кадр стека поверх кадра main().
  5. В этот новый кадр помещаются:
    *   Параметр `base` (копия значения `primitiveValue`, т.е. `10`).
    *   Локальная переменная `multiplier` (значение `2`).
    *   Локальная переменная `result` (значение `20`).
  1. После завершения calculateScore() ее кадр удаляется из стека. Значение result возвращается в main() и присваивается переменной score (которая находится в кадре main()).
  2. После завершения main() его кадр также удаляется. Объект User в куче становится кандидатом на удаление сборщиком мусора (GC), так как на него больше нет активных ссылок.

Особенности в Android (Java/Kotlin)

  • Не путать с "стеком" как контейнером для View-элементов. В контексте памяти речь идет именно о call stack.
  • Каждый поток имеет свой собственный стек. Вот почему локальные переменные потоко-безопасны (видны только в одном потоке выполнения).
  • Переполнение стека (StackOverflowError) происходит при слишком глубокой рекурсии или бесконечных вызовах методов, когда стек исчерпывает выделенную ему память.
  • В Kotlin с появлением корутин важно понимать, что приостановленные функции (suspend functions) не блокируют поток, а их состояние сохраняется в продолжениях (continuations), которые управляются диспетчером корутин, а не напрямую стеком потока в классическом понимании.

Итог: В стеке хранятся локальные примитивы и ссылки на объекты, что обеспечивает быстрый доступ и автоматическое управление временем жизни данных в рамках метода. Объекты всегда создаются в куче, что позволяет гибко управлять памятью, но требует работы сборщика мусора. Понимание этого различия критически важно для написания эффективных и стабильных Android-приложений, особенно при работе с производительностью и избегании утечек памяти.