Где живёт постоянный пул Java String, куча или стек? - PullRequest
96 голосов
/ 07 февраля 2011

Мне известна концепция пула констант и пула констант String, используемых JVM для обработки литералов String. Но я не знаю, какой тип памяти используется JVM для хранения константных литералов String. Стек или куча? Поскольку это литерал, который не связан ни с одним экземпляром, я предполагаю, что он будет храниться в стеке. Но если он не ссылается ни на один экземпляр, литерал должен быть собран прогоном GC (поправьте меня, если я ошибаюсь), так как это обрабатывается, если он хранится в стеке?

Ответы [ 6 ]

66 голосов
/ 07 февраля 2011

Технически нет ответа. Согласно спецификации виртуальной машины Java, область для хранения строковых литералов находится в пуле констант времени выполнения . Область памяти пула констант времени выполнения выделяется для каждого класса или интерфейса, поэтому она вообще не привязана ни к одному экземпляру объекта. Пул констант времени выполнения является подмножеством области метода , в которой «хранятся структуры для класса, такие как пул констант времени выполнения, данные полей и методов, а также код для методов и конструкторов, включая специальные методы, используемые в инициализация класса и экземпляра и инициализация типа интерфейса ". В спецификации VM говорится, что, хотя область метода логически является частью кучи, она не предписывает, чтобы память, выделенная в области метода, подвергалась сборке мусора или другим способам поведения, которые были бы связаны с обычными структурами данных. выделено в кучу.

50 голосов
/ 07 января 2013

Как объясняется этим ответом , точное местоположение пула строк не указано и может варьироваться от одной реализации JVM к другой.

Интересно отметить, что до Java 7 пул находился в постоянном пространстве кучи JVM горячей точки, но он был перемещен в основную часть кучи начиная с Java 7 :

Площадь : HotSpot
Краткое содержание : В JDK 7 интернированные строки больше не выделяются в постоянной генерации кучи Java, а вместо этого выделяются в основной части кучи Java (известной как young и старое поколение), наряду с другими объектами, созданными приложением. Это изменение приведет к увеличению объема данных, находящихся в основной куче Java, и уменьшению объема данных в постоянной генерации, что может потребовать корректировки размеров кучи. Большинство приложений увидят только относительно небольшие различия в использовании кучи из-за этого изменения, но более крупные приложения, которые загружают много классов или интенсивно используют метод String.intern (), увидят более существенные различия. RFE: 6962931

А в Java 8 Hotspot постоянное поколение полностью удалено.

29 голосов
/ 07 февраля 2011

Строковые литералы не хранятся в стеке.

Строковые литералы (или, точнее, объекты String, которые их представляют) являются , исторически хранились в куче, называемой "permgen"куча.(Permgen - это сокращение от постоянного поколения.)

При нормальных обстоятельствах строковые литералы и большая часть других вещей в куче permgen доступны "постоянно" и не собирают мусор.(Например, литералы String всегда доступны из объектов кода, которые их используют.) Однако вы можете настроить JVM для поиска и сбора динамически загружаемых классов, которые больше не нужны, и это может привести к тому, что литералы String будут собираться мусором..

РАЗЪЯСНЕНИЕ № 1 - Я не говорю, что Пермген не получает GC '.Обычно это происходит, когда JVM решает запустить Full GC.Я хочу сказать, что строковые литералы будут доступны, пока код, который их использует, доступен, и код будет доступен, пока доступен загрузчик классов кода, а для загрузчиков классов по умолчанию это означает "навсегда ".

РАЗЪЯСНЕНИЕ # 2 - Фактически, Java 7 хранит внутренние объекты String в обычной куче.Это включает (я предполагаю) объект String, который представляет строковые литералы.(Подробности см. В ответе @ assylias.)

23 голосов
/ 28 ноября 2013

Объединение строк

Объединение строк (иногда также называемое канонизацией строк) процесс замены нескольких объектов String равным значением, но другая идентичность с одним общим объектом String. Вы можете достичь эта цель, сохраняя свою собственную карту (возможно, софт или слабые ссылки в зависимости от ваших требований) и использование карты значения как канонизированные значения. Или вы можете использовать метод String.intern () который предоставлен вам JDK.

Иногда в Java 6 использование String.intern () было запрещено многими стандарты из-за высокой возможности получить исключение OutOfMemoryException, если объединение вышло из-под контроля. Oracle Java 7 реализация строки пул был значительно изменен. Вы можете посмотреть детали в http://bugs.sun.com/view_bug.do?bug_id=6962931 и http://bugs.sun.com/view_bug.do?bug_id=6962930.

String.intern () в Java 6

В те старые добрые времена все интернированные строки хранились в PermGen. - часть кучи фиксированного размера, в основном используемая для хранения загруженных классов и пул строк. Помимо явно интернированных строк, строка PermGen В пуле также содержатся все литеральные строки, ранее использовавшиеся в вашей программе (здесь используется важное слово - если класс или метод никогда не был загружен / вызван, любые константы, определенные в нем, не будут загружены).

Самой большой проблемой с таким пулом строк в Java 6 было его местоположение - ПермГен. PermGen имеет фиксированный размер и не может быть расширен в во время выполнения. Вы можете установить его, используя опцию -XX: MaxPermSize = 96m. Насколько я знаю, что размер PermGen по умолчанию варьируется от 32M до 96M в зависимости от Платформа. Вы можете увеличить его размер, но его размер все равно будет фиксированный. Такое ограничение требовало очень осторожного использования String.intern - вам лучше не проверять любой неконтролируемый пользовательский ввод, используя этот метод. Вот почему пулы строк во времена Java 6 были в основном реализованы в карты, управляемые вручную.

String.intern () в Java 7

Инженеры Oracle сделали чрезвычайно важное изменение в строке логика пула в Java 7 - пул строк был перемещен в кучу. Это означает, что вы больше не ограничены отдельным фиксированным размером область памяти. Все строки теперь расположены в куче, как и большинство других обычные объекты, что позволяет управлять только размером кучи, а настройка вашего приложения. Технически, одного этого может быть достаточно причина пересмотреть использование String.intern () в ваших программах на Java 7. Но есть и другие причины.

Значения пула строк собирают мусор

Да, все строки в пуле строк JVM пригодны для мусора коллекция, если нет ссылок на них из корней вашей программы. Это относится ко всем обсуждаемым версиям Java. Это означает, что если ваш интернированная строка вышла из области видимости и других ссылок на это - это будет сборщик мусора из пула строк JVM.

Право на сборку мусора и проживание в куче, JVM пул строк, кажется, подходящее место для всех ваших строк, не так ли? Теоретически это правда - неиспользуемые строки будут собирать мусор из пул, используемые строки позволят вам сэкономить память на тот случай, если вы получить равную строку из ввода. Кажется, идеальное воспоминание стратегия сохранения? Почти так. Вы должны знать, как выглядит пул строк выполняется до принятия каких-либо решений.

источник.

10 голосов
/ 17 февраля 2018

Как объясняют другие ответы Память в Java делится на две части

1. Стек: Для каждого потока создается один стек, в котором хранятся кадры стека, в которых снова хранятся локальные переменные, а если переменная является ссылочным типом, то эта переменная ссылается на область памяти в куче для фактического объекта.

2. Куча: Все виды объектов будут создаваться только в куче.

Память кучи снова разделена на 3 части

1. Молодое поколение: Хранит объекты с короткой жизнью, само Молодое поколение можно разделить на две категории Пространство Эдема и Пространство выживших .

2. Старое поколение: Храните объекты, которые пережили много циклов сборки мусора и на которые все еще ссылаются.

3. Постоянное поколение: Хранит метаданные о программе, например, постоянный пул времени выполнения.

Пул строковых констант принадлежит области постоянной генерации памяти кучи.

Мы можем видеть пул констант времени выполнения для нашего кода в байт-коде, используя javap -verbose class_name, который покажет нам ссылки на методы (#Methodref), объекты класса (#Class), строковые литералы (#String)

runtime-constant-pool

Подробнее об этом можно прочитать в моей статье Как JVM обрабатывает внутреннюю перегрузку и переопределение методов .

7 голосов
/ 25 июля 2017

К отличным ответам, которые уже включены здесь, я хочу добавить то, чего не хватает в моей перспективе - иллюстрацию.

Как вы уже JVM делит выделенную память для Java-программы на две части. один стек , а другой куча . Стек используется для целей выполнения, а куча - для целей хранения. В этой кучной памяти JVM выделяет некоторую память, специально предназначенную для строковых литералов. Эта часть динамической памяти называется пул строковых констант .

Так, например, если вы инициируете следующие объекты:

String s1 = "abc"; 
String s2 = "123";
String obj1 = new String("abc");
String obj2 = new String("def");
String obj3 = new String("456);

Строковые литералы s1 и s2 перейдут в пул строковых констант, объекты obj1, obj2, obj3 - в кучу. На все они будут ссылаться из стека.

Также обратите внимание, что «abc» появится в куче и в пуле строковой константы. Почему String s1 = "abc" и String obj1 = new String("abc") будут созданы таким образом? Это потому, что String obj1 = new String("abc") явно создает новый и референтно отдельный экземпляр объекта String, а String s1 = "abc" может повторно использовать экземпляр из пула строковых констант, если он доступен. Для более подробного объяснения: https://stackoverflow.com/a/3298542/2811258

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...