предисловие
JVM
Область памяти включает в себяСчетчик ПК,Стек виртуальной машины Java,собственный стек методов,куча,область метода,постоянный пул времени выполненияипрямая память.
В этой статье в основном представлены функции и характеристики каждой области памяти, а также описаны возможности и типы исключений переполнения памяти в каждой области.
текст
(1) Область памяти JVM
Java
выполнение виртуальной машиныJava
В процессе работы программы управляемая память делится на несколько разныхобласть данных. Каждая из этих областей памяти имеет свое назначение, а также время их создания и уничтожения. Некоторые области существуют с началом процесса виртуальной машины, а некоторые области создаются и уничтожаются с началом и окончанием пользовательских потоков.
JVM
область памятитакже известен какJava
область данных времени выполнения. К ним относятся:счетчик команд,стек виртуальных машин,собственный стек методов,куча,Статический метод области,статический постоянный пулЖдать.
Примечание. Счетчик программ, стек виртуальной машины и собственный стек методов принадлежат каждомуприватный поток; области кучи и методов принадлежатОбщий доступ к потоку.
1.1 Счетчик ПК
счетчик команд (Program Counter Register
) это кусокменьшепространство памяти, его роль можно рассматривать какТекущий потоквыполненныйиндикатор номера строки байт-кода.
- Байт-код, выполняемый текущим потокоминдикатор номера строки.
- У каждой нити своя
PC
прилавок. - нитьчастный, жизненный цикл такой же, как у потока, с
JVM
родился, чтобы начать,JVM
Закрой и умри. - выполнение потока
Java
метод, запишите виртуальную машину, на которой он выполняетсяадрес инструкции байт-кода. - выполнение потока
Native
метод, счетчик регистрируется какнулевой(Undefined
). - только в
Java
Спецификация виртуальной машины не указывает никакихOutOfMemoryError
район ситуации.
1.2 Стек виртуальной машины Java
нитьчастныйПространство памяти, его жизненный цикл такой же, как у потока. Во время выполнения потока метод создается при выполнении каждого метода.Рамка стекадля хранениятаблица локальных переменных,стек операндов,динамическая ссылка,экспорт методаи другая информация.
- таблица локальных переменных
- стек операндов
- динамическая ссылка
- экспорт метода
Процесс от вызова до завершения каждого метода соответствуеткадр стекав стеке виртуальной машиныНажмите на стекиПопвесь процесс.
Объясни в свою очередькадр стекаКонкретная структура и функция четырех составляющих элементов в:
1). Локальная переменная таблица
таблица локальных переменныхэто группаПеременнаяскладских помещений для храненияпараметры методаилокальная переменная. существуетClass
Лист метода файловCode
атрибутmax_locals
указывает таблицу локальных переменных, требуемую методомМаксимальная емкость.
таблица локальных переменныхМесто в памяти выделяется во время компиляции и может быть сохраненовремя компиляцииРазличные типы переменных:
-
базовый тип данных:
boolean
,byte
,char
,short
,int
,float
,long
,double
Ждать8
своего рода; -
тип ссылки на объект:
reference
, указывая на объектначальный адресизуказатель ссылки; -
тип обратного адреса:
returnAddress
тип обратного адреса.
Переменный слот (Variable Slot
):
переменный слотдатаблица локальных переменныхизнаименьшая единица, указанный размер
32
немного. за64
немногоlong
иdouble
переменная, виртуальная машина присваивает ейдва последовательныхизSlot
космос.
2) Стек операндов
стек операндов(Operand Stack
), также известный как стек операций, представляет собой стек по принципу «последним пришел – первым ушел». существуетClass
документCode
атрибутmax_stacks
Указывает максимальную глубину стека во время выполнения.Java
виртуальная машинаМеханизм выполнения интерпретацииназываетсямеханизм выполнения на основе стека, что относится ккучаозначает-стек операндов.
- итаблица локальных переменныхТакой же,стек операндовтакже
32
длина словаМассив единиц. - Виртуальная машина может быть сохранена в подсчете операторатип данных:
int
,long
,float
,double
,reference
иreturnType
Тип ожидания (дляbyte
,short
а такжеchar
Значение типа также преобразуется вint
). - итаблица локальных переменныхОтличие в том, что это не попоказательдля доступа, но через стандартныйоперация стека—толкать стекиПоппосещать. Например, если инструкция помещает значение в стек операндов, другая инструкция может извлечь это значение, чтобы использовать его позже.
Виртуальная машина использует стек операндов в качестве своей рабочей области — большинство инструкций извлекают данные отсюда, выполняют операцию и помещают результат обратно в стек операндов..
begin
iload_0 // push the int in local variable 0 onto the stack
iload_1 // push the int in local variable 1 onto the stack
iadd // pop two ints, add them, push result
istore_2 // pop int, store into local variable 2
end
на этопоследовательность байт-кода, первые две инструкции
iload_0
иiload_1
будет храниться втаблица локальных переменныхиндекс0
и1
помещается в стек операндов, за которым следуетiadd
Инструкция извлекает два целых числа из стека операндов, складывает их и помещает результат встек операндов. Четвертая директиваistore_2
затем изстек операндоввставьте результат и сохраните его втаблица локальных переменныхиндекс2
позиция.
Рисунок ниже иллюстрирует этот процесс в деталяхтаблица локальных переменныхистек операндовизменения состояния (не используется на рисунке)таблица локальных переменныхистек операндовобласть отмечена пробелом).
3) Динамическая ссылка
каждыйкадр стекасодержит указатель на среду выполненияпостоянный пулпринадлежащийссылка на метод, эта ссылка сохраняется для поддержки вызовов методов во времядинамическая ссылка.
Class
документпостоянный пулСуществует большое количествоСимволическая ссылка, в байт-кодеинструкция вызова методаПринять направленный на метод постоянного бассейнаСимволическая ссылкаявляется параметром. Эти символы относятся к:
- Статический анализ: часть его будет вэтап загрузки классаили при первом использовании он преобразуется впрямая цитата(как
final
,static
домен и т. д.), называемыйстатический анализ, - Динамический разбор: другая часть будетво время операциипреобразоваться впрямая цитата, называетсядинамическая ссылка.
4) Адрес возврата метода
Когда метод начинает выполняться, есть только два способа выйти из текущего метода:
-
нормальная доходность: Когда выполнение встречает инструкцию возврата, возвращаемое значение будет передано вышестоящему вызывающему методу.Этот метод выхода вызываетсяЗавершите экспорт в обычном режиме(
Normal Method Invocation Completion
), в общем,PC
прилавокМожет использоваться как обратный адрес. -
возврат исключения: при выполнении исключения метод и текущее тело не обрабатываются, это приведет к выходу из метода, а затем нет возвращаемого значения, вызываемогоЭкспорт аварийного завершения(
Abrupt Method Invocation Completion
), обратный адрес должен быть передан черезобработчик исключенийтаблица для определения.
Когда метод возвращается, следующее может произойти в последовательности3
операции:
- восстанавливатьсяМетод верхнего уровняизтаблица локальных переменныхистек операндов.
- Пучоквозвращаемое значениезапихивать вКадр стека Callorизстек операндов.
- будет
PC
прилавокзначение указывает наследующийМестоположение Директива Местоположение.
резюме:
Уведомление: В Спецификации виртуальной машины Java для этой области указаны два исключения.один: если глубина стека, запрошенная текущим потоком, превышает глубину, разрешенную стеком виртуальной машины,
StackOverflowError
Исключение (в случае, когда стек виртуальной машины не допускает динамического расширения);Второй: Если расширение не может примениться к достаточному объему памяти, оно выдастOutOfMemoryError
аномальный.
1.3 Стек собственных методов
собственный стек методовиJava
стек виртуальных машиниграет очень похожую роль, главное отличиеJava
стек виртуальных машинказненJava
Сервис метода,исобственный стек методоввоплощать в жизньNative
Сервис метода(обычно пишется на С).
Некоторые версии выпуска виртуальных машин (например,
Sun HotSpot
виртуальная машина) напрямуюсобственный стек методовиJava
виртуальная машинаСтеки два в одном. Как и стек виртуальной машины, собственный стек методов также создаетStackOverflowError
иOutOfMemoryError
аномальный.
1.4. Кучи
Java
куча принадлежитобмен потокамиизмаксимумОбласть памяти, которая создается при запуске виртуальной машины. Единственная цель этой области памяти состоит в том, чтобыЭкземпляр объекта хранения, почти все экземпляры объектов выделяют здесь память.
существуетJava
, куча делится на две отдельные области:Кайнозой (Young Generation
),старость (Old Generation
).Кайнозой (Young
) делится на три области:ОдинEden
Район идваSurvivor
Площадь -From Survivor
Район иTo Survivor
Площадь.
Краткое резюме: новые объекты размещаются первыми в молодом поколении (
Young Generation
) изEden
округ,Survivor
Район какEden
Район иOld
буфер области, вSurvivor
Объекты в районе, пережившие несколько коллекций, будут переданы старому поколению.Old
середина.
Цель этого подразделения состоит в том, чтобы сделатьJVM
Может лучше управлять объектами в памяти кучи, включая выделение и переработку памяти.
1.5 Область метода
область метода иJava
Как и куча, разделяемая несколькими потоками, она используется для храненияинформация о классе,постоянный,статическая постояннаяиСвоевременно скомпилированный кодданные.
1.6 Пул констант времени выполнения
Пул констант времени выполненияобласть методачасть,Class
За исключением файлаверсия класса,поле,методиинтерфейсВ дополнение к информации описания,
Другим видом информации являетсяпостоянный пул, используется для хранения различных сгенерированных при компиляциибуквальныйиСимволическая ссылка.
1.7 Прямая память
прямая памятьне является частью области данных среды выполнения виртуальной машины и неJava
Область памяти, указанная в спецификации виртуальной машины.Java NIO
позволятьJava
Программа прямого доступапрямая память,как правилопрямая памятьбудет быстрее, чемКуча памяти Java. Поэтому для сценариев с частыми операциями чтения и записи и высокими требованиями к производительности можно рассмотреть возможность использования прямой памяти.
(2) Общие исключения переполнения памяти
Помимо счетчика программ,Java
Возможны другие области выполнения виртуальной машиныOutOfMemoryError
Исключения проверяются следующим образом:
2.1 Переполнение кучи Java
Java
В куче могут храниться экземпляры объектов. Постоянно создавая объекты и гарантируяGC Roots
Есть до целевого пути, чтобы избежать сборки мусора для очистки объектов.
Когда предел емкости достигнет максимального количества объектов в куче, будетOutOfMemoryError
аномальный.
настраиватьJVM
Параметры запуска:-Xms20M
установить кучуМинимум памятиза20M
,-Xmx20M
установить кучуМаксимальная памятьиМинимум памятито же, это предотвращаетJava
Куча закончилась памятиАвтоматическое расширение.-XX:+HeapDumpOnOutOfMemoryError
Параметр может сделать виртуальную машину в исключении переполнения памятиDump
внекуча памятиМоментальные снимки времени выполнения.
HeapOOM.java
/**
* VM Args: -Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
*/
public class HeapOOM {
public static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<>();
while (true) {
list.add(new OOMObject());
}
}
}
Результаты тестового запуска:
ОткрытымJava VisualVM
экспортHeap
время выполнения памятиdump
документ.
HeapOOM
Объекты продолжают создаваться, использование динамической памяти достигает99%
.уборщик мусораПытался постоянно перерабатывать, но не удалось.
Анализ: В этом случае обычно необходимо учитыватьутечка памятиипереполнение памятиДве возможности.
- Если это утечка памяти:
дальнейшее использование
Java VisualVM
инструменты для анализа, просмотрпросочившийся объектчерез что路径
иGC Roots
относится куборщик мусоране может быть переработан.
- В случае переполнения памяти:
пройти через
Java VisualVM
Анализ инструмента, нет протекающего объекта, то естькуча памятиОбъекты должны выжить. Следует рассмотреть следующие меры:
- Проверить существование какого-либо объекта из кодажизненный цикл слишком длинный,Устойчивое состояние слишком долгослучае попробуйте уменьшить память времени выполнения программы.
- Проверьте виртуальную машинупараметры кучи(
-Xmx
и-Xms
), по сравнению с машинойфизическая памятьПосмотрите, можно ли его масштабировать.
2.2 Виртуальная машина и переполнение стека нативных методов
Что касается стека виртуальной машины и стека собственных методов, могут быть следующие два типа исключений памяти:
- По запросу на сайтеглубина стекабольше, чем позволяет виртуальная машинамаксимальная глубина, бросит
StackOverflowError
аномальный. - Если виртуальная машинаСтек расширенийне может подать заявку на достаточноОЗУпространство, может бросить
OutOfMemoryError
аномальный.
Это можно разделить на два типа проблем: когда пространство стека не может быть выделено, когда память стека находится в концеслишком маленький,все ещеИспользовалстек памятислишком большой.
Исключение StackOverflowError
План испытаний один:
- использовать
-Xss
уменьшение параметрастек памятиемкость, печатается при возникновении исключениякучаглубина. - определить многоелокальная локальная переменная, чтобы увеличитькадр стекасерединатаблица локальных переменныхдлина.
настраиватьJVM
Параметры запуска:-Xss128k
настраиватьстек памятиРазмер128k
.
JavaVMStackSOF.java
/**
* VM Args: -Xss128k
*/
public class JavaVMStackSOF {
private int stackLength = 1;
private void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) {
JavaVMStackSOF oom = new JavaVMStackSOF();
try {
oom.stackLeak();
} catch (Throwable e) {
System.out.println("Stack length: " + oom.stackLength);
throw e;
}
}
}
Результаты теста:
Анализ: В рамках одного потока либокадр стека слишком великвсе ещеЕмкость стека виртуальной машины слишком мала, когда память не может быть выделена, виртуальная машина выдает
StackOverflowError
аномальный.
Второй план тестирования:
- продолжай создаватьнитьи держите поток в рабочем состоянии.
JavaVMStackOOM.java
/**
* VM Args: -Xss2M
*/
public class JavaVMStackOOM {
private void running() {
while (true) {
}
}
public void stackLeakByThread() {
while (true) {
new Thread(new Runnable() {
@Override
public void run() {
running();
}
}).start();
}
}
public static void main(String[] args) {
JavaVMStackOOM oom = new JavaVMStackOOM();
oom.stackLeakByThread();
}
}
Результаты теста:
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
Приведенный выше тестовый код имеет большой риск при запуске, что может привести к зависанию операционной системы, поэтому я не буду тестировать его здесь сам, а приведу результаты тестирования автора.
2.3 Разлитая зона метода и время работы бассейна
(1) Тест на переполнение постоянной памяти пула во время выполнения
константы времени выполненияибуквальныйхранятся впостоянный пул времени выполнения, постоянный пул также является частью области методов, поэтому тесты для обеих областей одинаковы.
используется здесьString.intern()
провести тестирование:
String.intern() — это нативный метод, его функция такова: если в пуле констант строк есть строка объекта String, то напрямую вернуть объект String в пул констант; В противном случае поместите строку, содержащуюся в этом объекте String, в пул констант и верните ссылку на этот объект String.
настраиватьJVM
Параметры запуска: пройти-XX:PermSize=10M
и-XX:MaxPermSize=10M
ограничениеобласть методаразмер10M
, тем самым косвенно ограничиваяпостоянный пулемкость.
RuntimeConstantPoolOOM.java
/**
* VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
*/
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
// 使用List保持着常量池的引用,避免Full GC回收常量池
List<String> list = new ArrayList<>();
// 10MB的PermSize在Integer范围内足够产生OOM了
int i = 0;
while (true) {
list.add(String.valueOf(i++).intern());
}
}
}
Анализ результатов испытаний:
JDK1.6
Результаты запуска версии:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
JDK1.6
отображение результатов работы версиипостоянный пулпереполнится и выброситпостоянный ременьизOutOfMemoryError
аномальный.
иJDK1.7
и выше не получат того же результата, он будет продолжать зацикливаться.
(2) Тест на переполнение памяти области метода
область методаClass
соответствующую информацию, такую какимя класса,модификатор доступа,постоянный пул,описание поля,Описание методаЖдать.
заПереполнение памяти в области методаОсновная идея состоит в том, чтобы генерировать большое количествобайт-код классазаполнение областиобласть метода.
импортировать сюдаSpring
обрамленныйCGLib
динамический прокситехнология байт-кода, через цикл, чтобы постоянно генерировать новыепрокси-класс,достигатьобласть методаЭффект переполнения памяти.
JavaMethodAreaOOM.java
/**
* VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
*/
public class JavaMethodAreaOOM {
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
enhancer.create();
}
}
private static class OOMObject {
public OOMObject() {
}
}
}
JDK1.6
Результаты запуска версии:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
Анализ результатов испытаний:
JDK1.6
отображение результатов работы версиипостоянный пулпереполнится и выброситпостоянный ременьизOutOfMemoryError
аномальный.
иJDK1.7
и выше не получит того же результата, он будет продолжать зацикливаться.
2.4. Прямое переполнение памяти
роднойпрямая памятьемкость может проходить через-XX:MaxDirectMemorySize
указан, если не указан, то значение по умолчанию такое же, какJava
кучамаксимальное значение(указан -Xmx) то же самое.
сценарии тестирования:
Получено непосредственно через отражениеUnsafe
Пример применения к операционной системе для выделения памяти посредством отражения:
настраиватьJVM
Параметры запуска:-Xmx20M
уточнитьJava
максимальная память кучи,-XX:MaxDirectMemorySize=10M
уточнитьпрямая памятьразмер.
DirectMemoryOOM.java
/**
* VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M
*/
public class DirectMemoryOOM {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) throws Exception {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
while (true) {
unsafe.allocateMemory(_1MB);
}
}
}
Результаты теста:
Анализ результатов испытаний:
Зависит отDirectMemory
Возникающее в результате переполнение памяти, очевидной особенностью являетсяHeap Dump
В файле не будет видно никакой очевидной информации об исключении.
еслиOOM
после того, как это случилосьDump
Файл небольшой и используется прямо или косвенно в программеNIO
, то можете рассмотреть этот вопрос.
Добро пожаловать в технический публичный аккаунт: Zero One Technology Stack
Эта учетная запись будет продолжать делиться сухими товарами серверных технологий, включая основы виртуальных машин, многопоточное программирование, высокопроизводительные фреймворки, асинхронное ПО, промежуточное ПО для кэширования и обмена сообщениями, распределенные и микросервисы, материалы для обучения архитектуре и расширенные учебные материалы и статьи.