Где живет постоянный пул Java String, куча или стек?

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

Ответ 1

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

Ответ 2

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

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

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

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

Ответ 3

Строковые литералы не хранятся в стеке. Никогда. На самом деле в стеке не хранятся никакие объекты.

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

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

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

ПОЯСНЕНИЕ № 2 - На самом деле Java 7 и более поздние версии используют обычную кучу для хранения пула строк. Таким образом, объекты String, представляющие литералы String и строки intern'd, фактически находятся в обычной куче. (См. @Assylias Ответ для деталей.)


Но я все еще пытаюсь найти тонкую грань между хранилищем строкового литерала и строкой, созданной с помощью new.

Там нет "тонкой линии". Это действительно очень просто:

  • String объекты, которые представляют/соответствуют строковым литералам, содержатся в пуле строк.
  • String объекты, созданные с помощью вызова String::intern, хранятся в пуле строк.
  • Все остальные объекты String НЕ хранятся в пуле строк.

Тогда возникает отдельный вопрос о том, где хранится пул строк. До Java 7 это была куча permgen. Начиная с Java 7 это основная куча.

Ответ 4

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

Объединение строк (иногда также называемое канонизацией строк) является процесс замены нескольких объектов 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 пул также содержал все литеральные строки, ранее использовавшиеся в вашей программе (здесь используется важное слово - если класс или метод никогда не был загруженный/вызванный, любые константы, определенные в нем, не будут загружены).

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

String.intern() в Java 7

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

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

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

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

источник.

Ответ 5

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

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

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

Куча памяти снова делится на 3 порции

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

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

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

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

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

runtime-constant-pool

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

Ответ 6

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

Как вы уже знаете, 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 s1 = "abc" а String s1 = "abc" может повторно использовать экземпляр из пула строковых констант, если таковой имеется. Для более подробного объяснения: fooobar.com/info/30727/...

enter image description here