Где хранятся примитивные поля?
Примитивные поля хранятся как часть объекта, который где-то создается . Самый простой способ подумать, где это - это куча. Однако это не всегда так. Как описано в теории и практике Java: городские легенды производительности, вновь :
JVM могут использовать метод, называемый escape-анализом, с помощью которого они могут сказать, что определенные объекты остаются ограниченными одним потоком в течение всего их времени жизни, и что время жизни ограничено временем жизни данного стекового фрейма. Такие объекты можно безопасно размещать в стеке, а не в куче. Более того, для небольших объектов JVM может полностью оптимизировать распределение и просто поднять поля объекта в регистры.
Таким образом, помимо высказывания «объект создан, и поле там тоже», нельзя сказать, находится ли что-то в куче или в стеке. Обратите внимание, что для небольших короткоживущих объектов возможно, что «объект» не будет существовать в памяти как таковой, и вместо этого его поля могут быть помещены непосредственно в регистры.
Статья заканчивается:
JVM удивительно хороши в выяснении вещей, которые мы привыкли считать, что только разработчик может знать. Позволяя JVM выбирать между распределением стека и распределением кучи в каждом конкретном случае, мы можем получить выигрыш в производительности при распределении стека, не заставляя программиста мучиться с тем, стоит ли выделять его в стеке или в куче.
Таким образом, если у вас есть код, который выглядит так:
void foo(int arg) {
Bar qux = new Bar(arg);
...
}
где ...
не позволяет qux
покинуть эту область, вместо этого qux
может быть размещен в стеке. Это на самом деле выигрыш для виртуальной машины, потому что это означает, что ей не нужно собирать мусор - он исчезнет, когда покинет область видимости.
Подробнее об анализе побега в Википедии. Для тех, кто хочет вникать в статьи, Escape Analysis for Java от IBM. Для тех, кто пришёл из мира C #, вы можете найти хорошие чтения Эрика Липперта «Стоп - это деталь реализации» и «Правда о типах значений» (они полезны для типов Java, так как многие концепции и аспекты одинаковы или похожи) , Почему в книгах .Net говорится о распределении стека и кучи памяти? и идет в это.
По стэку и куче
В кучу
Итак, зачем вообще стек или куча? Для вещей, которые выходят за рамки, стек может быть дорогим. Рассмотрим код:
void foo(String arg) {
bar(arg);
...
}
void bar(String arg) {
qux(arg);
...
}
void qux(String arg) {
...
}
Параметры тоже являются частью стека. В ситуации, когда у вас нет кучи, вы будете передавать полный набор значений в стеке. Это хорошо для "foo"
небольших строк ... но что произойдет, если кто-то поместит в эту строку огромный XML-файл. Каждый вызов будет копировать всю огромную строку в стек - и это будет довольно расточительно.
Вместо этого лучше поместить объекты, у которых есть некоторая жизнь, за пределы непосредственной области видимости (переданные в другую область видимости, застрявшие в структуре, которую поддерживает другой человек и т. Д.) В другую область, которая называется кучей.
В стеке
Вам не нужен стек. Можно гипотетически написать язык, который не использует стек (произвольной глубины). Старый бейсик, на котором я учился в юности, делал так: можно было делать только 8 уровней gosub
вызовов, и все переменные были глобальными - стека не было.
Преимущество стека состоит в том, что когда у вас есть переменная, которая существует с областью действия, когда вы покидаете эту область, этот кадр стека выталкивается. Это действительно упрощает то, что есть, а что нет. Программа переходит к другой процедуре, новому стековому фрейму; программа вернется к процедуре, и вы вернетесь к той, которая видит вашу текущую область; программа покидает процедуру, и все элементы в стеке освобождаются.
Это действительно облегчает жизнь человеку, пишущему среду выполнения кода, для использования стека и кучи. В них просто много концепций и способов работы с кодом, позволяющих человеку, пишущему код на языке, быть полностью свободным от мысли о них.
Природа стека также означает, что он не может стать фрагментированным. Фрагментация памяти - реальная проблема с кучей. Вы выделяете несколько объектов, затем мусор собираете средний, а затем пытаетесь найти место для следующего большого, который будет выделен. Это беспорядок. Возможность поместить вещи в стек вместо этого означает, что вам не нужно иметь с этим дело.
Когда что-то собирается
Когда что-то собирает мусор, его уже нет. Но это только сборщик мусора, потому что о нем уже забыли - в программе больше нет ссылок на объект, к которым можно получить доступ из текущего состояния программы.
Я укажу, что это очень большое упрощение сборки мусора. Существует много сборщиков мусора (даже в Java - вы можете настроить сборщик мусора, используя различные флаги ( документы ). Они ведут себя по-разному, и нюансы того, как каждый из них делает что-то, слишком глубоки для этого ответа. Вы можете прочитать Основы сборки мусора Java, чтобы лучше понять, как это работает.
Тем не менее, если что-то размещено в стеке, оно не является сборщиком мусора как часть System.gc()
- оно освобождается, когда всплывает кадр стека. Если что-то находится в куче и ссылается на что-либо в стеке, это не будет сборкой мусора в это время.
Почему это важно?
По большей части, это традиция. Написанные учебники, классы компиляторов и документация по различным битам имеют большое значение для кучи и стека.
Однако современные виртуальные машины (JVM и аналогичные) приложили много усилий, чтобы скрыть это от программиста. Если вам не хватает одного или другого и вам нужно знать почему (вместо того, чтобы просто увеличивать объем памяти), это не имеет большого значения.
Объект находится где-то и находится там, где к нему можно правильно и быстро получить доступ в течение соответствующего промежутка времени, в течение которого он существует. Если это в стеке или куче - это не имеет значения.