предисловиетекстГлобальный пул строк (пул строк также называется пулом строковых литералов)пул констант файла класса (пул констант класса)постоянный пул времени выполненияАссоциация между тремя постоянными пуламиСуммироватьСсылка на ссылку
предисловие
Память Java JVM можно разделить на три области: динамическая память (heap), стековая память (stack) и область методов (method), также называемая статической областью хранения.
В процессе обучения я часто слышу термин пул констант.В предыдущем разделе при сравнении данных с == я упоминал пул строковых констант.После запроса я узнал, чтоКонстантный пул не является ни кучей, ни стековой памятью., то пул констант может быть связан с областью методов.По этой причине прочитайте книгу «Введение в JVM», чтобы понять взаимосвязь между пулом констант и областью методов, а также иметь определенное представление о классификации констант бассейн.
Весь код в этой статье основан на JDK1.8.
текст
Прежде чем обсуждать типы константных пулов, необходимо понять, что такое константы.
- Переменные-члены, украшенные final, представляют собой константы, и после того, как значение задано, его нельзя изменить!
- Существует три типа переменных, изменяемых с помощью final: статические переменные, переменные экземпляра и локальные переменные, которые соответственно представляют три типа констант.
В распределении памяти Java всего 3 пула констант:
Глобальный пул строк (пул строк также называется пулом строковых литералов)
Где находится пул строковых констант в области памяти Java
- В JDK6.0 и более ранних версиях пул строковых констант помещается в область Perm Gen (то есть в область методов), а объекты в это время хранятся в пуле констант.
- В версии JDK7.0 пул строковых констант был перемещен в кучу. В этот момент пул констант сохраняет ссылку. В JDK8.0 постоянное поколение (область методов) было заменено метапространством.
Что такое пул строковых констант?
Функция пула строк, реализованная в HotSpot VM, представляет собой класс StringTable, представляющий собой хэш-таблицу с размером и длиной по умолчанию 1009; он хранит ссылки на резидентные строки (вместо самих резидентных строк). То есть после того, как эта StringTable ссылается на некоторые обычные экземпляры строк, они эквивалентны присвоению идентификатора «резидентных строк». Эта таблица StringTable существует только один раз для каждого экземпляра HotSpot VM и используется всеми классами.
StringTable по сути являетсяHashSet<String>
. Это чистая конструкция времени выполнения, которая поддерживается лениво. Обратите внимание, что он хранит только ссылку на экземпляр java.lang.String, а не содержимое объекта String. Обратите внимание, что он хранит только ссылку, по которой можно получить конкретный объект String.
В JDK6.0 длина StringTable фиксирована и равна 1009. Поэтому, если в пуле строк слишком много строк, это вызовет конфликт хэшей и приведет к слишком длинному связанному списку. #intern(), вам нужно будет перейти к связанному списку, чтобы найти один за другим, что приведет к значительному падению производительности;
В JDK7.0 длину StringTable можно указать с помощью параметров:
-XX:StringTableSize=66666
пул констант файла класса (пул констант класса)
Все мы знаем, что в дополнение к информации описания версии класса, поля, метода, интерфейса и т. д. в файле класса есть еще одна часть информации, которая является постоянной таблицей пула, которая используется для хранения информации, сгенерированной компилятор.各种字面量(Literal)和符号引用(Symbolic References)
.字面量比较接近 Java 语言层面常量的概念,如文本字符串、被声明为 final 的常量值等
. Символические ссылки относятся к концепции принципов компиляции, включая следующие три типа констант:
- Полные имена классов и интерфейсов
- Имена полей и дескрипторы
- имя и дескриптор метода
Каждая константа в пуле констант представляет собой таблицу. Существует 11 различных данных структуры таблицы, как показано в следующей таблице. Первый бит каждой таблицы представляет собой байтовый флаг (значение 1).-12, который представляет, какой тип константы является текущим. константа принадлежит.
Каждый тип константного типа имеет различную структуру. Конкретная структура не будет описываться в этой статье. Эта статья посвящена различению концепций этих трех константных пулов (если читатели хотят узнать больше о структуре данных каждого константного типа, пожалуйста, см. "Углубленное понимание". Содержание главы 6 виртуальной машины Java,
постоянный пул времени выполнения
Пул констант времени выполнения является частью области методов.
Когда файл Java компилируется в файл класса, то есть пул констант класса, упомянутый выше, будет сгенерирован, поэтому когда создается пул констант времени выполнения?
Когда JVM выполняет класс, он должен пройти через加载、连接、初始化
, а подключение включает в себя три этапа: проверку, подготовку и разрешение. Когда класс загружается в память, JVM будет хранить содержимое в пуле констант файла класса в пуле констант времени выполнения.Можно видеть, что пул констант времени выполнения также имеет по одному для каждого класса. Как упоминалось выше, то, что хранится в пуле констант класса,字面量和符号引用
, то есть они хранят не экземпляры объектов, а символьные ссылочные значения объектов. После разрешения, то есть замены символической ссылки прямой ссылкой, процесс синтаксического анализа будет запрашивать глобальный пул строк, то есть StringTable, упомянутую выше, чтобы убедиться, что строки, на которые ссылаются пул констант времени выполнения и глобальный пул строк Ссылки в согласуются.
Ассоциация между тремя постоянными пулами
Когда дело доходит до выполнения JVM, это также включает в себя字符串常量池
.
在类加载阶段, JVM 会在堆中创建对应这些 class 文件常量池中的字符串对象实例,并在字符串常量池中驻留其引用。具体在 resolve 阶段执行。这些常量全局共享。
Здесь это более общее, да, это фаза разрешения, но это не так, как все думают, объект создается немедленно, а ссылка находится в пуле строковых констант.Спецификация JVM явно указывает, что фаза разрешения может быть ленивой.
В файле класса в спецификации JVM есть два типа записей пула констант:CONSTANT_Utf8 和CONSTANT_String
. Первый тип представляет собой строковый тип в кодировке UTF-8, а второй представляет собой константный тип String, но он не содержит напрямую содержимое константы String, а содержит только индекс.Другая запись пула констант, указанная этим индексом, должна быть It является константой типа CONSTANT_Utf8, которая действительно содержит здесь содержимое строки.
В HotSpot VM в пуле констант времени выполнения
CONSTANT_Utf8 -> Symbol*(一个指针,指向一个Symbol类型的C++对象,内容是跟Class文件同样格式的UTF-8编码的字符串)
CONSTANT_String -> java.lang.String(一个实际的Java对象的引用,C++类型是oop)
CONSTANT_Utf8 будет создан во время загрузки класса, в то время как CONSTANT_Stringlazy resolve
Например, это будет разрешено только тогда, когда инструкция ldc, которая ссылается на элемент, выполняется в первый раз. Затем, прежде чем он разрешится, виртуальная машина HotSpot называет свой тип какJVM_CONSTANT_UnresolvedString
, содержимое просто индекс как в Class файле, после разрешения константный тип этого элемента станет окончательнымJVM_CONSTANT_String
, и содержимое становится фактическим oop.
Видя это, вы должны понимать.Что касается реализации HotSpot VM, при загрузке класса эти строковые литералы будут попадать в пул констант времени выполнения текущего класса и не будут попадать в глобальный пул строковых констант (то есть, в StringTable нет соответствующей ссылки, и в куче не создается соответствующий объект). Итак, как упоминалось выше, после разрешения он будет запрашивать глобальный пул строк и, наконец, заменять символическую ссылку прямой ссылкой. (То есть, хотя литералы и символические ссылки сохраняются в пуле констант времени выполнения при загрузке класса, для литералов отложенного разрешения определенные операции все равно будут выполняться после разрешения.)
О ленивом разрешении, вам нужно знать об инструкции ldc здесь
Проще говоря, он используется для помещения константного значения типа String из пула констант на вершину стека.
В качестве примера возьмем следующий код:
public static void main(String[] args) {
String s = "abc";
}
Например, файл кода Test.java, сначала откройте окно Dos в каталоге файла, выполнитеjavac Test.java
для компиляции, затем введитеjavap -verbose Test
Просмотрите его скомпилированный файл класса следующим образом:
Загрузите «abc» в начало стека операндов с помощью инструкции ldc, затем используйте astore_1, чтобы присвоить его локальной переменной s, которую мы определили, и выполнить возврат.
В сочетании с вышеизложенным на этапе разрешения (разрешение пула констант) создаются строковые литералы, а их ссылки находятся в пуле констант строк, но это разрешение лениво. Другими словами, нет реального объекта и нет реального объекта в пуле строковых констант, так как же инструкция ldc может поместить значение на вершину стека и выполнить операцию присваивания? Или подумайте об этом с другой стороны, поскольку фаза разрешения ленива, всегда будет время, когда она действительно будет выполнена, когда она?
Выполнение инструкции ldc является условием, запускающим действие отложенного разрешения.
Семантика выполнения байт-кода ldc здесь следующая: перейти к пулу констант времени выполнения текущего класса (пул констант времени выполнения, ConstantPool + ConstantPoolCache в виртуальной машине HotSpot), чтобы найти элемент, соответствующий индексу, если элемент не был разрешен, разрешить это и возвращает содержимое после разрешения.
При обнаружении константы типа String, если процесс разрешения обнаружит, что StringTable уже имеет ссылку на java.lang.String с совпадающим содержимым, он напрямую вернет ссылку; и наоборот, если нет ссылки на экземпляр String с совпадающим содержимым содержимого в StringTable, то объект String, соответствующий содержимому, будет создан в куче Java, затем ссылка будет записана в StringTable, и эта ссылка будет возвращена.
Можно видеть, что необходимость создания инструкцией ldc нового экземпляра String зависит от того, записала ли таблица StringTable ссылку на строку, соответствующую содержимому, при первом выполнении инструкции ldc.
Используйте следующий код для анализа и отображения:
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
String s3 = "xxx";
}
Просмотрите его скомпилированный файл класса следующим образом:
Покажите это графически:
String s1 = "abc";
Процесс разрешения обнаруживает, что в пуле строковых констант нет ссылки на «abc», он создает новый объект «abc» в куче, сохраняет ссылку на объект в пуле строковых констант, а затем возвращает ссылку на s1. .
String s2 = "abc";
Процесс разрешения обнаружит, что в StringTable уже есть ссылка на объект «abc», и вернет ссылку непосредственно на s2 без создания какого-либо объекта.
String s3 = "xxx";
Как и в первой строке кода, создайте объект в куче, сохраните ссылку на объект в StringTable и, наконец, верните ссылку на s3.
Постоянный пул и стажерский метод
public static void main(String[] args) {
String s1 = "ab";//#1
String s2 = new String(s1+"d");//#2
s2.intern();//#3
String s4 = "xxx";//#4
String s3 = "abd";//#5
System.out.println(s2 == s3);//true
}
Просмотрите его скомпилированный файл класса следующим образом:
Согласно информации файла класса, "ab", "d", "xxx" и "abd" вошли в постоянный пул файла класса. Поскольку класс ленив на этапе разрешения, он не будет создавать экземпляр объекта, он также не будет находиться в нем.
Схема выглядит следующим образом:
Введите основной метод и интерпретируйте каждую строку кода.
- 1. Инструкция ldc загрузит «ab» на вершину стека, другими словами, создаст объект «ab» в куче и сохранит ссылку на объект в пуле строковых констант.
- 2. Инструкция ldc загружает "d" в верхнюю часть стека, а затем выполняется операция объединения.Внутренне создается объект StringBuilder, добавляется полностью и, наконец, вызывается метод toString объекта StringBuilder, чтобы получить объект String (содержимое abd, обратите внимание на то, что метод toString создаст новый объект String) и назначьте его s2 (назначение s2 по-прежнему является ссылкой на объект).
注意此时没有把“abd”对象的引用放入字符串常量池。
- 3. Внутренний метод сначала обращается к пулу строковых констант, чтобы узнать, есть ли ссылка на объект «abd», если нет, то сохраняет ссылку на объект «abd» в куче в пул строковых констант и возвращает значение. ссылка, но мы не используем переменную для ее получения.
- 4, бессмысленно, просто чтобы проиллюстрировать, что литерал «abd» в файле класса — это то, что вы получаете, когда # 5.
- 5. В пуле строковых констант уже есть ссылка на объект «abd», поэтому ссылка напрямую возвращается в s3.
Суммировать
1. В каждой ВМ существует только одна копия глобального пула строковых констант, в которой хранится эталонное значение строковых констант.
2. Пул констант класса доступен для каждого класса во время компиляции На этапе компиляции сохраняются различные литералы и символические ссылки.
3. Пул констант времени выполнения предназначен для сброса значения ссылки на символ в пуле констант каждого класса в пул констант времени выполнения после загрузки класса, то есть каждый класс имеет пул констант времени выполнения, и класс находится в константе времени выполнения. После синтаксического анализа символическая ссылка заменяется прямой ссылкой, которая согласуется со значением ссылки в глобальном пуле констант.
4. Строковый литерал в пуле констант файла класса входит в пул констант времени выполнения при загрузке класса и сохраняет ссылку на строку в пуле констант строк, когда он фактически находится в фазе разрешения (т. е. когда ldc). Кроме того, пул констант среды выполнения является динамическим по отношению к пулу констант файла класса. Некоторые константы не обязательно генерируются во время компиляции, т. е. содержимое, не заданное в пуле констант файла класса, может введите пул констант времени выполнения в области методов.Метод intern хранит строковую константу в пуле строковых констант и пуле констант времени выполнения (что касается того, какой пул констант вводить первым, я думал, что пул строковых констант следует вводить первым, и конкретная реализация также надеется на совет от Бога).