предисловие
Эта серия JVM относится к некоторым пунктам знаний, обобщенным в моем процессе обучения. Цель состоит в том, чтобы позволить читателям быстрее освоить пункты знаний, связанные с JVM. Неизбежно, что будет некоторый акцент. Если вы хотите изучать знания JVM более систематически и в деталях, вам все равно нужно перейти к чтению профессиональных книг и документов.
Тема этой статьи:
- Обзор областей памяти JVM
- Как распределяется пространство в области кучи? Демонстрация переполнения кучи
- Как выделяется память для создания нового объекта?
- Область метода для метапространства
- Что такое кадр стека? Что находится в кадре стека? Как понять?
- собственный стек методов
- счетчик команд
- Что такое кэш кода?
Примечание. Пожалуйста, различайте две разные концепции структуры памяти JVM (схема памяти) и JMM (модель памяти Java)!
Обзор
Память является очень важным системным ресурсом, промежуточным хранилищем и мостом между жестким диском и ЦП и обеспечивает работу операционной системы и приложений в режиме реального времени. Схема памяти JVM определяет стратегию использования, распределения и управления памятью Java во время выполнения процесса, что обеспечивает эффективную и стабильную работу JVM.
На приведенном выше рисунке показана текущая классическая схема памяти Java. (Область кучи рисуется на 2333 меньше, что в принципе должно быть самой большой площадью)
Если он классифицируется в зависимости от того, являются ли потоки общими или нет, как показано на следующем рисунке:
PS: Независимо от того, являются ли потоки общими или нет, после фактического понимания фактического использования каждой области естественно помнить. Нет необходимости в механическом запоминании.
Давайте рассмотрим каждую область ниже.
1. Куча (область кучи)
1.1 Введение в область кучи
Сначала поговорим о кучах. Куча является наиболее распространенной областью сбоев OOM. Это самая большая область в области памяти и используется всемиобмен потоками, хранитсяПочти всеобъекты-экземпляры, массивы.Все экземпляры объектов и массивы размещаются в куче, но по мере развития компиляторов JIT сметоды анализа побегаПостепенно зрелая технология оптимизации распределения стека и скалярной замены приведет к некоторым тонким изменениям,Все объекты размещаются в куче, постепенно становясь менее «абсолютными»..
Расширенные знания: часть оптимизации JIT-компиляции -анализ побега.
Рекомендуемое чтение:Глубокое понимание escape-анализа в Java.
Куча Java — это основная область, управляемая сборщиком мусора, поэтомуЧасто упоминается как «куча GC».. С точки зрения восстановления памяти, поскольку сборщик в основном использует алгоритм сбора поколений, куча Java также может быть подразделена на:Молодой и старый. Более подробныйПространство Эдем, Из пространства выжившего, В пространство выжившегоЖдать. С точки зрения распределения памяти куча Java, совместно используемая потоками, может быть разделена на несколько частных буферов выделения потока (Thread Local Allocation Buffer, TLAB). Однако, как бы он ни был разделен, это не имеет никакого отношения к содержимому хранилища, независимо от того, в какой области экземпляр объекта все равно хранится, цель дальнейшего разделения — лучше высвободить память или быстрее выделить память.
1.2 Настройка области кучи
Согласно Спецификации виртуальной машины Java,Куча Java может находиться в физически прерывистом пространстве памяти., если он логически непрерывен, как и наше дисковое пространство. При реализации он может быть либо реализован как фиксированный размер, либо динамически корректироваться во время выполнения.
Как настроить?
Установив следующие параметры, вы можете установить начальное значение и максимальное значение области кучи, например-Xms256M -Xmx 1024M
,в-X
Эта буква означает, что это параметр времени выполнения JVM,ms
даmemory start
Аббревиатура, китайское значение - начальное значение памяти,mx
даmemory max
Аббревиатура, означающая максимальную память.
Стоит отметить, что в нормальных условиях пространство кучи продолжает увеличиваться и уменьшаться во время работы сервера, что вызывает ненужную нагрузку на систему.Xms
иXmx
Будет установлен тот же размер, чтобы избежать дополнительного давления при изменении размера кучи после GC.
1.3 Распределение пространства в куче по умолчанию
Кроме того, еще раз подчеркните общую ситуацию с выделением памяти в куче.
Кто-то может спросить здесь, откуда вы это знаете? Если я хочу настроить это соотношение, как мне его изменить?
Позвольте мне сначала рассказать вам, как увидеть конфигурацию виртуальной машины по умолчанию. Выполните следующую команду в командной строке, чтобы просмотреть все параметры JVM по умолчанию для текущей версии JDK.
java -XX:+PrintFlagsFinal -version
вывод
Соответствующий вывод должен содержать несколько сотен строк.Давайте посмотрим на два параметра, связанных с выделением памяти в куче.
>java -XX:+PrintFlagsFinal -version
[Global flags]
...
uintx InitialSurvivorRatio = 8
uintx NewRatio = 2
...
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
Описание параметра
параметр | эффект |
---|---|
-XX:InitialSurvivorRatio | Начальное соотношение пространства Эдем/Выживший нового поколения |
-XX:NewRatio | Соотношение памяти старой области/молодой области |
Поскольку новое поколение состоит из Эдема + S0 + S1, в соответствии с приведенным выше соотношением по умолчанию, если размер памяти области Эдема составляет 40 МБ, то две выжившие области составляют 5 МБ, вся молодая область составляет 50 МБ, а затем память размер старой области можно рассчитать как 100M, общий размер кучи 150M.
1.4 Демонстрация переполнения кучи
/**
* VM Args:-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
* @author Richard_Yi
*/
public class HeapOOMTest {
public static final int _1MB = 1024 * 1024;
public static void main(String[] args) {
List<byte[]> byteList = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
byte[] bytes = new byte[2 * _1MB];
byteList.add(bytes);
}
}
}
вывод
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid32372.hprof ...
Heap dump file created [7774077 bytes in 0.009 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at jvm.HeapOOMTest.main(HeapOOMTest.java:18)
-XX:+HeapDumpOnOutOfMemoryError
Это позволяет JVM выводить информацию из кучи при обнаружении исключения OOM, что особенно важно для исключений OOM, возникающих с разницей в несколько месяцев.
Создать новый объект Процесс выделения памяти
Прочитав приведенное выше введение в кучу, давайте куй железо, пока горячо, а затем изучите процесс выделения памяти JVM для создания нового объекта.
Большинство объектов генерируются в районе Эдена. Когда область Эдена полна, она будет вызватьYoung Garbage Collection
,СейчасYGC
. Во время сборки мусора стратегия очистки реализуется в области Eden, а объекты, на которые нет ссылок, напрямую перерабатываются. Объекты, которые еще живы, будут перемещены в зону выживших. Выживший разделен на два пространства памяти, так и s1. КаждыйYGC
Когда уцелевший объект копируется в неиспользуемое пространство, используемое в настоящее время пространство полностью очищается, а статус использования двух пространств меняется. еслиYGC
Переносимый объект больше, чем верхний предел вместимости области Survivor, и он напрямую передается старому поколению. Объект не может вечно оставаться в новом поколении, как человек становится взрослым в 18 лет, в JVM-XX:MaxTenuringThreshold
Параметр предназначен для настройки порога перехода объекта из молодого поколения в старое поколение. Значение по умолчанию15, можно повысить до старости после 14 обменов в зоне Выживший.
Вышеупомянутые термины связаны с некоторой сборкой мусора, незнакомые читатели могут обратиться к информации или прочитать главы этой серии, посвященные сбору мусора.
2. MetAscace.
В HotSpot JVM,Постоянная генерация ( ≈ область метода)используется вМетаданные и константный пул для хранения классов и методов,НапримерClass
иMethod
. Всякий раз, когда класс загружается в первый раз, его метаданные помещаются в постоянное поколение.
Постоянное поколение имеет ограничение по размеру, поэтому, если загружено слишком много классов, это может привести к переполнению памяти в постоянном поколении, что является злом.java.lang.OutOfMemoryError: PermGen
, для чего нам пришлось настроить виртуальную машину.
Так почему же PermGen был перемещен из HotSpot JVM в Java 8? (Видеть:JEP 122: Remove the Permanent Generation):
- Так как память PermGen часто переполняется, вызывая раздражающие
java.lang.OutOfMemoryError: PermGen
, так что разработчики JVM надеются, что этим участком памяти можно будет управлять более гибко, чтобы такой ООМ не возникал часто - Удаление PermGen может облегчить интеграцию HotSpot JVM с JRockit VM, поскольку JRockit не имеет постоянного поколения.
В конечном итоге PermGen был удален по разным причинам, указанным выше.Область метода перемещается в метапространство, а пул строковых констант перемещается в область кучи..
Если быть точным, в Пермской областиПул строковых констант был перемещен в память кучиСередина после Java7, когда Java 8, PermGen заменяется метапространством,Другой контент, такой как метаинформация, поля, статические свойства, методы, константы и т. д., перемещается в область метапространства.. Напримерjava/lang/Object
Метаинформация класса, статические свойстваSystem.out
, целочисленная константа100000
Ждать.
Суть метапространства аналогична постоянному поколению и представляет собой реализацию области методов в спецификации JVM. Но самая большая разница между метапространством и постоянной генерацией заключается в следующем:Метапространство находится не в виртуальной машине, а использует локальную память. Поэтому по умолчанию размер метапространства ограничен только локальной памятью. (Как и в случае с прямой памятью, упомянутой ниже, используется локальная память)
In JDK 8, classes metadata is now stored in the native heap and this space is called Metaspace.
Соответствующие параметры настройки JVM:
параметр | эффект |
---|---|
-XX:MetaspaceSize | Начальный размер (в байтах), выделенный для Metaspace |
-XX:MaxMetaspaceSize | Максимальное значение, выделенное для метапространства. При превышении этого значения будет запущен полный сборщик мусора. По умолчанию это значение не ограничено, но оно должно зависеть от размера системной памяти. JVM динамически изменяет это значение. |
-XX:MinMetaspaceFreeRatio | После GC минимальный процент оставшейся емкости пространства Metaspace, уменьшающий сборку мусора, вызванную выделением пространства. |
-XX:MaxMetaspaceFreeRatio | После GC процент максимальной оставшейся емкости метапространства, уменьшающий сборку мусора, вызванную освобождением места. |
Дополнительная литература: Две хорошие статьи о Metaspace.
3. Стек виртуальной машины Java
Для каждого потока JVM создает отдельный стек при создании потока. Другими словами, жизненный цикл стека виртуальной машины такой же, как и у потока, и он является частным для потока. За исключением метода Native, метод Java реализует процесс вызова и выполнения через стек виртуальной машины Java (требуется взаимодействие программной технологии, кучи и данных в метапространстве). Таким образом, стек виртуальной машины Java является одним из ядер исполнительного механизма виртуальной машины. Элементы, которые извлекаются в стек в стеке виртуальной машины Java, называются «фреймами стека».
Фрейм стека — это структура данных, используемая для поддержки вызовов и выполнения методов виртуальной машиной. Фрейм стека хранит таблицу локальных переменных метода, стек операндов, динамическое соединение и адрес возврата метода, а также другую информацию. Процесс каждого метода от вызова до завершения выполнения соответствует процессу перемещения кадра стека в стек в стеке виртуальной машины.
Стек соответствует потоку, кадр стека соответствует методу
В активном потоке действителен только кадр наверху стека, называемыйтекущий кадр стека. Выполняемый метод называетсятекущий метод. Когда исполнительный механизм работает, все инструкции могут работать только с текущим кадром стека. иStackOverflowError
просьбапереполнение стека, вызывая исчерпание памяти, обычно встречающееся в рекурсивных методах.
Стек виртуальной машины выполняет арифметическую обработку активного фрейма стека, соответствующего каждому методу, с помощью pop и push, и после окончания нормального выполнения метода обязательно переходит к другому фрейму стека. В процессе выполнения, если возникнет исключение, будет выполнен откат исключения, а адрес возврата определяется таблицей обработки исключений.
Видно, что кадр стека занимает высокое положение во всей системе JVM. Далее также подробно представлена информация о хранении в кадре стека.
1. Таблица локальных переменных
Таблица локальных переменныхОбласть для хранения параметров метода и локальных переменных, определенных внутри метода..
Объем памяти, требуемый таблицей локальных переменных, выделяется во время компиляции.При входе в метод полностью определяется, сколько места локальной переменной этот метод должен выделить во фрейме, и размер таблицы локальных переменных не будет изменен во время выполнения метода. исполнение..
Перейдите непосредственно к коду здесь для лучшего понимания.
public int test(int a, int b) {
Object obj = new Object();
return a + b;
}
Если локальная переменная представляет собой 8 основных базовых типов данных Java, она существует в таблице локальных переменных, если это ссылочный тип. Например, новая строка, ссылка хранится в таблице локальных переменных, а экземпляр находится в куче.
2. Стек операций
Стек операндовИз названия вы можете понять, что это стековая структура. Механизм выполнения интерпретации виртуальной машины Java называется «механизм выполнения на основе стека», а упомянутый «стек» - это стек операндов. Когда JVM создает кадр стека для метода,кадр стекасоздать метод встек операндов, чтобы гарантировать, что инструкция в методе может завершить работу.
Или понять это практически.
/**
* @author Richard_yyf
*/
public class OperandStackTest {
public int sum(int a, int b) {
return a + b;
}
}
Скомпилируйте и сгенерируйте.class
После напильника разобрать и посмотреть инструкцию по сборке
> javac OperandStackTest.java
> javap -v OperandStackTest.class > 1.txt
public int sum(int, int);
descriptor: (II)I
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3 // 最大栈深度为2 局部变量个数为3
0: iload_1 // 局部变量1 压栈
1: iload_2 // 局部变量2 压栈
2: iadd // 栈顶两个元素相加,计算结果压栈
3: ireturn
LineNumberTable:
line 10: 0
3. Динамическое подключение
Каждый кадр стека содержит один в постоянном пулессылка на текущий метод, Цель состоит в том, чтобыПоддержка динамического связывания процесса вызова метода.
4. Адрес возврата метода
При выполнении метода возможны две ситуации выхода:
- Обычный выход, то есть нормальное выполнение инструкции возврата байт-кода любого метода, например
RETURN
,IRETURN
,ARETURN
Ждать - ненормальный выход
Возврат к текущему методу метода независимо от условия выходаодеяломесто вызова. Процесс выхода из метода эквивалентен извлечению текущего фрейма стека.Есть три способа выхода:
- Возвращаемое значение помещается в верхний фрейм стека вызовов.
- Сообщение об исключении, отправленное вв состоянии справитьсякадр стека
- Счетчик ПК указывает на следующую инструкцию после вызова метода
Дальнейшее чтение:Схема набора машинных инструкций JVM
В-четвертых, локальный стек методов
Стек собственных методов (Native Method Stack) и стек виртуальной машины играют очень похожую роль.Разница между ними заключается в том, что стек виртуальной машины служит виртуальной машине для выполнения методов Java (то есть байт-кода), а собственный стек методов обслуживает собственные методы, используемые виртуальной машиной.. В спецификации виртуальной машины нет обязательных условий для языка, использования и структуры данных методов в собственном стеке методов, поэтому конкретная виртуальная машина может свободно реализовать это. Даже некоторые виртуальные машины (например, виртуальная машина Sun HotSpot) напрямую объединяют собственный стек методов и стек виртуальной машины в один. Как и стек виртуальных машин,Область стека собственного метода также вызывает исключения StackOverflowError и OutOfMemoryError..
5. Счетчик программ
Регистр счетчика программ представляет собой небольшое пространство памяти. является частным потоком.Его можно рассматривать как индикатор номера строки байт-кода, выполняемого текущим потоком.. Что это обозначает?
Стандартная версия: поскольку код выполняется в потоке, поток может быть приостановлен. Другими словами, ЦП некоторое время выполняет поток А, а поток А приостанавливается до завершения его выполнения, затем выполняет поток В и, наконец, снова выполняет поток А. ЦП должен знать, какую часть инструкций выполнять потоку. A, и счетчик потоков сообщит процессору.
Поскольку многопоточность виртуальной машины Java достигается за счетПотоки по очереди переключаются и распределяют время выполнения процессора для достижения этой цели.Да, ЦП может работать, только если данные загружены в регистры. В регистре хранится локальная информация, связанная с инструкцией.Из-за ограничения кванта времени ЦП, много потоков в параллельном процессе выполнения,В любой момент процессор или ядро многоядерного процессора будут выполнять только одну инструкцию в потоке..
Следовательно, чтобы восстановить правильную позицию выполнения после переключения потоков, каждый поток должен иметь независимый программный счетчик, а счетчики между каждым потоком не влияют друг на друга и хранятся независимо. После того, как каждый поток будет создан, он будет генерировать свой счетчик программ и кадр стека.Счетчик программ используется для хранения смещения выполняемой инструкции и индикатора номера строки.Выполнение или восстановление потока зависит от счетчика программ. Исключения нехватки памяти также не возникают в этой области.
6. Прямая память
Прямая память (Direct Memory) не является частью области данных среды выполнения виртуальной машины и не является областью памяти, определенной в спецификации виртуальной машины Java. Но эта часть памяти также используется часто и может вызывать исключения OutOfMemoryError, поэтому мы приводим ее здесь для объяснения.
В JDK 1.4 недавно добавлен класс NIO (новый ввод/вывод), который вводит метод ввода/вывода, основанный на каналах (Channel) и буферах (Buffer), который можетИспользуйте библиотеку собственных функций для прямого выделения памяти вне кучи., а затем передатьОбъекты DirectByteBuffer, хранящиеся в куче Java, обрабатываются как ссылки на эту память.. Это может значительно повысить производительность в некоторых сценариях, посколькуИзбегайте копирования данных между кучей Java и собственной кучей и обратно..
Очевидно, что выделение нативной прямой памяти не будет ограничено размером Java-кучи, но поскольку это память, то оно определенно будет ограничено размером всей нативной памяти (включая ОЗУ и область подкачки или файл подкачки). и адресное пространство процессора. OOM также может возникнуть, если сумма областей памяти превышает предел физической памяти.
Code Cache
короче,Кэш кода JVM — это область, в которой JVM хранит свой байт-код как собственный код.. Мы называем каждый блок исполняемого нативного кодаnmethod
. Долженnmethod
Может быть полным или встроенным методом Java.
Компилятор «точно в срок» (JIT) является крупнейшим потребителем области кэша кода. Вот почему некоторые разработчики называют эту память кэшем JIT-кода.
Пространство памяти, занимаемое этой частью кода, становится областью CodeCache. В общем, эта часть области нас не волнует, и большинство разработчиков не знакомы с этой областью. Если эта область OOM, вы увидите это в журналеjava.lang.OutOfMemoryError code cache
.
Опции диагностики
опции | По умолчанию | описывать |
---|---|---|
PrintCodeCache | false | Следует ли печатать использование CodeCache перед выходом из JVM |
PrintCodeCacheOnCompilation | false | Следует ли печатать использование области CodeCache после JIT-компиляции каждого метода. |
дальнейшее чтениеIntroduction to JVM Code Cache
Ссылаться на
- «Глубокое понимание виртуальной машины Java» — Чжоу Чжимин
- «Эффективность кода»
- Metaspace in Java 8
- Схема набора машинных инструкций JVM
- Introduction to JVM Code Cache
Если эта статья была вам полезна, я надеюсь поставить лайк, это самая большая мотивация для меня.