Для Java-программистов с помощью механизма автоматического управления памятью виртуальной машины нет необходимости писать парный код удаления/освобождения для каждой новой операции, а вызвать утечку памяти и проблемы с переполнением непросто. При возникновении утечек и переполнений памяти устранение неполадок может стать чрезвычайно сложной задачей без понимания того, как виртуальная машина использует память.
область данных времени выполнения
В процессе выполнения Java-программы виртуальная машина Java делит управляемую ею память на несколько различных областей данных. Согласно «Спецификации виртуальной машины Java (Java SE 7 Edition)», память, управляемая виртуальной машиной Java, будет включать следующие области данных времени выполнения, как показано на рисунке:
счетчик команд
Регистр счетчика программ — это небольшая область памяти, которую можно рассматривать как индикатор номера строки байт-кода, выполняемого текущим потоком. В концептуальной модели виртуальной машины интерпретатор байт-кода выбирает следующую инструкцию байт-кода для выполнения, изменяя значение программного счетчика во время работы.Требуются базовые функции, такие как ветвление, цикл, переход, обработка исключений и восстановление потока. счетчик для завершения.
Поскольку многопоточность виртуальной машины Java реализуется путем переключения потоков по очереди и распределения времени выполнения процессора, в любой момент времени процессор (в отличие от многоядерного процессора) будет выполнять инструкции только в одном потоке. Поэтому, чтобы восстановить правильную позицию выполнения после переключения потоков, каждый поток должен иметь независимый программный счетчик. Счетчики между потоками не влияют друг на друга и хранятся независимо. Мы называем этот тип области памяти «приватным потоком». память. .
Если поток выполняет метод Java, счетчик записывает адрес выполняемой инструкции байт-кода виртуальной машины; если поток выполняет собственный метод, значение счетчика пусто. Эта область памяти является единственной областью, в которой не указано условие OutOfMemeryError в спецификации виртуальной машины Java.
Стек виртуальной машины Java
Стеки виртуальных машин Java (стеки виртуальных машин Java) также являются потокозависимыми и имеют тот же жизненный цикл, что и потоки. Стек виртуальной машины описывает модель памяти выполнения метода Java; каждый метод создает кадр стека (Stack Frame) для хранения такой информации, как таблица локальных переменных, стек операндов, динамическая ссылка, выход из метода и т. д. Процесс каждого метода от вызова до завершения выполнения соответствует процессу помещения кадра стека в стек в стеке виртуальной машины.
В спецификации виртуальной машины java для этой области указаны два исключительных условия: если глубина стека, запрошенная потоком, больше, чем глубина, разрешенная виртуальной машиной, будет выброшено исключение StackOverflowError; если виртуальная машина может быть динамически расширена (большинство виртуальных машин Java можно динамически расширять, а также допускается использование стека виртуальных машин фиксированной длины.) Если во время расширения невозможно применить достаточно памяти, будет выдано исключение OutOfMemoryError.
собственный стек методов
Стек собственных методов предназначен для того, чтобы виртуальная машина выполняла службы собственных методов. Язык, использование и структура данных, используемые стеком собственных методов, не указаны в спецификации виртуальной машины, поэтому конкретная виртуальная машина может свободно реализовать их.
Виртуальная машина Sun HotSpot сочетает в себе собственный стек методов и стек виртуальной машины. Как и стек виртуальной машины, собственный стек методов также создает исключения StackOverflowError и OutOfMemoryError.
куча Java
Для большинства приложений куча Java является самой большой частью памяти, управляемой виртуальной машиной Java. Куча Java — это область памяти, разделяемая всеми потоками, создаваемая при запуске виртуальной машины, и здесь размещаются практически все экземпляры объектов и массивы. Куча Java — это основная область, управляемая сборщиком мусора, поэтому ее часто называют «кучей GC».
Согласно Спецификации виртуальной машины Java, куча Java может находиться в физически прерывистом пространстве памяти, если она логически непрерывна. При реализации он может быть реализован как фиксированный размер или расширяемый, но текущие основные виртуальные машины реализуются в соответствии с расширением (управляемым -Xmx и -Xms). Если в куче нет памяти для завершения выделения экземпляра и куча не может расшириться, будет выдано исключение OutOfMemoryError.
область метода
Область методов (Method Area) — это область памяти, совместно используемая каждым потоком, которая используется для хранения таких данных, как информация о классе, константы, статические переменные и код, скомпилированный JIT-компилятором, который был загружен виртуальной машина. Хотя спецификация виртуальной машины Java описывает область методов как логическую часть кучи, у нее есть псевдоним, называемый «не-кучей», который следует отличать от кучи Java.
Спецификация виртуальной машины Java имеет очень слабые ограничения на область методов, за исключением того, что она не требует непрерывной памяти, такой как куча Java, и может выбирать фиксированный размер или расширяемый размер.Вы также можете отказаться от реализации сборки мусора. Цель высвобождения памяти в этой области в основном направлена на высвобождение константного пула и выгрузки типа.В общем говоря, "оценка" восстановления этой области относительно неудовлетворительна, особенно выгрузка типа, условия довольно суровые, но рекультивация этой части территории неудовлетворительна, она действительно необходима.
В соответствии со спецификацией виртуальной машины Java, когда область методов не может соответствовать требованиям к выделению памяти, будет выдано исключение OutOfMemoryError.
постоянный пул времени выполнения
Пул констант времени выполнения является частью области методов. В дополнение к информации описания версии класса, поля, метода, интерфейса и т.д. в файле класса существует также константный пул (Constant Pool Table), который используется для хранения различных литералов и символьных ссылок, сгенерированных во время компиляции. Эта часть содержимого будет храниться в работающем пуле констант, который входит в область методов после загрузки класса.
Пул констант времени выполнения является динамическим.Язык Java не требует, чтобы константы генерировались только компилятором, то есть содержимое пула констант, не предустановленное в файле класса, может входить в пул констант времени выполнения области методов. Константы помещаются в пул, и эта функция в основном используется методом intern() класса String.
прямая память
Прямая память (Direct Memory) не является частью области данных времени выполнения виртуальной машины и не является областью памяти, определенной в Спецификации виртуальной машины Java, но эта часть памяти также часто используется и также может вызывать исключения OutOfMemoryError.
В JDK 1.4 был недавно добавлен класс NIO и введен метод ввода-вывода, основанный на канале и буфере, который может напрямую выделять память вне кучи с помощью библиотеки собственных функций, а затем сохранять ее в куче Java. объект работает как ссылка на этот фрагмент памяти. Это может значительно повысить производительность в некоторых сценариях за счет предотвращения копирования данных между кучей Java и собственной кучей.
Очевидно, что выделение нативной прямой памяти не будет ограничено размером кучи Java, но, поскольку это память, оно определенно будет ограничено общим размером нативной памяти и адресным пространством процессора.
Если сумма каждой области памяти превышает предел физической памяти, во время динамического расширения возникает исключение OutOfMemoryError.
Как виртуальная машина HotSpot обрабатывает размещение объектов, расположение и доступ
размещение объектов
Когда виртуальная машина встречает новую инструкцию, она сначала проверяет, могут ли параметры этой инструкции найти символическую ссылку на класс в пуле подконстант, и проверяет, был ли загружен, разрешен и обработан ли класс, представленный этой символической ссылкой. инициализирован. Если нет, то сначала необходимо выполнить соответствующий процесс загрузки класса. После прохождения проверки загрузки класса виртуальная машина выделяет память для создаваемого объекта. Размер требуемой объекту памяти можно полностью определить после загрузки класса.Задача выделения места под объект эквивалентна делению определенного объема памяти из кучи Java.
выделение памяти объекта:
1. Коллизия указателя
Если предположить, что память в куче Java абсолютно регулярна, вся используемая память размещена с одной стороны, свободная память размещена с другой стороны, а указатель размещен посередине как индикатор точки разграничения, то выделенная память - это просто указатель Переместите расстояние, равное размеру объекта, в сторону свободного места.
2. Бесплатный список
Если память в куче Java не является регулярной, а используемая память и свободная память чередуются, виртуальная машина должна вести список, записывать, какие блоки памяти доступны, и находить достаточно большой блок из списка при выделении пространства. к экземплярам объекта, и записи в списке обновляются.
Выбор метода распределения определяется тем, является ли куча Java обычной, а регулярность кучи Java определяется тем, имеет ли используемый сборщик мусора функцию уплотнения. Следовательно, при использовании коллектора с процессом Compact, например Serial и ParNew, принятый системой алгоритм распределения — коллизия указателей; при использовании коллектора на основе алгоритма Mark-Sweep, такого как CMS, обычно используется свободный список.
Как решить потокобезопасность при распределении:
В параллельной ситуации может случиться так, что память выделяется объекту A, указатель не успел измениться, а объект B одновременно использует исходный указатель для выделения памяти. Виртуальная машина решает проблему безопасности потоков двумя способами:
1. Синхронизируйте действие по выделению памяти — фактически виртуальная машина использует CAS вкупе с неудачной повторной попыткой для обеспечения атомарности операции обновления;
2. Действие по выделению памяти разделено на разные пространства в соответствии с потоками, то есть каждый поток предварительно выделяет небольшой кусок памяти в куче Java, который называется Thread Local Allocation Buffer (TLAB). Когда поток хочет выделить память, она выделяется в TLAB потока.Только когда TLAB израсходован и выделяется новый TLAB, требуется блокировка синхронизации. Использование виртуальной машиной TLAB можно указать с помощью параметра -XX:+/-UseTLAB.
После завершения выделения памяти виртуальная машина должна инициализировать выделенное пространство памяти до нулевого значения (исключая заголовок объекта).Если используется TLAB, этот рабочий процесс также может быть выполнен до выделения TLAB. Этот шаг гарантирует, что поля экземпляра объекта могут использоваться непосредственно в Java-коде без присвоения начальных значений, а программа может получить доступ к нулевым значениям, соответствующим типам данных этих полей.
Затем виртуальная машина должна выполнить необходимые настройки для объекта, например, экземпляром класса является этот объект, как найти информацию о метаданных класса, хэш-код объекта, возраст создания GC объекта и другую информацию. . Эта информация хранится в заголовке объекта. В зависимости от текущего состояния виртуальной машины, например от включения блокировки смещения, заголовок объекта будет установлен по-разному.
После завершения вышеуказанной работы с точки зрения виртуальной машины создан новый объект, но с точки зрения Java-программы создание объекта только началось — метод init еще не выполнен, и все поля по-прежнему равны нулю. Вообще говоря (определяется тем, следует ли байт-код за инструкцией invokespecial), метод init будет выполняться после выполнения новой инструкции, и объект будет инициализирован в соответствии с пожеланиями программиста, так что будет полностью сгенерирован действительно пригодный для использования объект.
Структура памяти объекта
В виртуальной машине HotSpot расположение объектов, хранящихся в памяти, можно разделить на три области: заголовок объекта (Header), данные экземпляра (Instance Data) и заполнение выравнивания.
заголовок объекта
Заголовок объекта виртуальной машины HotSpot включает в себя две части информации, которые используются для хранения данных времени выполнения самого объекта и указателя типа.
1. Сохраните данные времени выполнения самого объекта
Например, хэш-код, возраст создания сборщика мусора, флаг состояния блокировки, блокировка, удерживаемая потоком, смещенный идентификатор потока, смещенная отметка времени и т. д. Длина этой части данных указана в 32-разрядных и 64-разрядных виртуальных машинах (без указателей сжатия). ) Они 32-битные и 64-битные соответственно, официально называются "Mark Word". Объект должен хранить много данных времени выполнения, которые превысили предел, который может быть записан 32-битными и 64-битными структурами Bitmap, но информация заголовка объекта — это дополнительные затраты на хранение, которые не имеют ничего общего с данными. определяется самим объектом с учетом эффективности пространства виртуальной машины. , Mark Word разработан как нефиксированная структура данных, чтобы хранить как можно больше информации в очень небольшом пространстве памяти, он будет повторно использовать свое собственное пространство для хранения по состоянию объекта.
2. Введите указатель
То есть объект указывает на указатель на метаданные своего класса, и виртуальная машина использует этот указатель, чтобы определить, экземпляром какого класса является объект. Не все реализации виртуальных машин должны сохранять указатели типов данных объекта, другими словами, поиск информации метаданных для объекта не обязательно проходит через сам объект.
данные экземпляра
Данные экземпляра — это действующая информация, которую фактически хранит объект, а также это содержимое полей различных типов, определенных в программном коде. Независимо от того, унаследовано ли оно от родительского класса или определено в подклассе, оно должно быть задокументировано. На порядок хранения этой части будет влиять порядок, в котором параметры и поля стратегии выделения виртуальных машин определены в исходном коде Java.
Заполните
Заполнение его не обязательно существует и не имеет особого значения, оно просто выступает в роли заполнителя. Поскольку система автоматического управления памятью HotSpot VM требует, чтобы начальный адрес объекта был целым числом, кратным 8 байтам, иными словами, размер объекта должен быть целым числом, кратным 8 байтам. Поскольку часть заголовка объекта представляет собой целое число, кратное 8 байтам, когда часть данных экземпляра объекта не выровнена, ее необходимо дополнить выравниванием.
место доступа к объекту
Объекты создаются для того, чтобы использовать объекты, а программам на Java необходимо манипулировать определенными объектами в куче с помощью справочных данных в стеке. Поскольку ссылочный тип указывает ссылку на объект в спецификации виртуальной машины Java, он не определяет, как ссылка должна найти и получить доступ к определенному местоположению объекта в куче, поэтому метод доступа к объекту также зависит от реализации виртуальной машины. .из.
Текущие основные методы доступа — это использование дескрипторов и прямых указателей. следующим образом:
1. Управление доступом
Часть памяти будет разделена на пул дескрипторов в куче Java, а адрес дескриптора объекта хранится в ссылке, а дескриптор содержит конкретную информацию об адресе данных экземпляра объекта и данных типа.
2. Прямой доступ по указателю
Компоновка объекта кучи Java должна учитывать соответствующую информацию о том, как разместить данные типа, а адрес объекта хранится непосредственно в ссылке.
Самым большим преимуществом использования доступа к дескриптору является то, что стабильный адрес дескриптора сохраняется в ссылке.Когда объект перемещается (перемещение объектов очень распространено во время сборки мусора), изменяется только указатель данных экземпляра дескриптора, а сама ссылка не нуждается в модификации.
Самым большим преимуществом использования прямого доступа к указателю является то, что он быстрее и экономит время на позиционирование указателя.Поскольку доступ к объектам очень часто используется в Java, это также очень объективная стоимость выполнения. Виртуальная машина Sun HotSpot использует прямые указатели для доступа к объектам.
Исключение OutOfMemoryError
В описании спецификации виртуальной машины Java помимо счетчика программ несколько других областей времени выполнения в памяти виртуальной машины могут иметь исключение OutOfMemoryError.
Переполнение кучи Java
Куча Java используется для хранения экземпляров объектов. Пока объекты создаются непрерывно и существует доступный путь между корнями GC и объектами, чтобы избежать очистки этих объектов механизмом сборки мусора, память будет генерироваться, когда количество объектов достигает предел максимальной емкости кучи Исключение переполнения.
/**
* VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*/
public class HeapOOM {
static class OOMObject{}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
while(true){
list.add(new OOMObject());
}
}
}
результат операции:
Чтобы устранить аномалию в этой области, общий метод заключается в том, чтобы сначала проанализировать моментальный снимок дампа кучи из дампа с помощью инструмента анализа образа памяти Утечка памяти или переполнение памяти. Если это утечка памяти, вы можете дополнительно использовать инструмент для проверки цепочки ссылок просочившегося объекта на корни GC. Затем можно выяснить, как утекшие объекты связаны с корнями GC, и предотвратить их автоматический сбор сборщиком мусора. Если утечки нет, то есть объекты в памяти должны быть еще живы, то следует проверить параметры кучи виртуальной машины, сравнить ее с физической памятью машины, можно ли ее увеличить, и проверить из кода, есть ли какие-то жизненные циклы объекта. Если состояние слишком длинное и состояние удерживается слишком долго, попытайтесь уменьшить потребление памяти во время выполнения программы.
Переполнение стека виртуальной машины и собственного стека методов
Поскольку в виртуальной машине HotSpot стек виртуальной машины и локальный стек методов не различаются, для HotSpot, хотя параметр -Xoss (задающий размер локального стека методов) существует, на самом деле он недействителен, а контейнер стека только управляется настройкой параметра -Xss.
/**
* VM Args;-Xss160k
*/
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak(){
stackLength++;
stackLeak();
}
public static void main(String[] args) {
JavaVMStackSOF oom = new JavaVMStackSOF();
oom.stackLeak();
}
}
Область метода и переполнение пула констант времени выполнения
String.intern() — это собственный метод, его функция такова: если пул строковых констант уже содержит строку, равную этому объекту String, он возвращает объект String, представляющий строку в пуле, в противном случае возвращается этот объект String. содержащаяся строка добавляется в пул констант, и возвращается ссылка на этот объект String. В JDK 1.6 и более ранних версиях, поскольку пул констант выделяется в постоянном поколении, мы можем ограничить размер области метода с помощью -XX:PermSize и -XX:MaxPermSize, тем самым косвенно ограничивая емкость пула констант.
/**
* VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M
*/
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
int i = 0;
while (true){
list.add(String.valueOf(i).intern());
}
}
}
При работе в JDK 1.8 появляется следующее приглашение, и переполнения памяти нет:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=10M; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=10M; support was removed in 8.0
Поискав информацию в интернете нашел следующий абзац, поясняющий причину:
Метаданные класса, пул строк и статические переменные класса будут удалены из постоянного поколения и помещены в кучу Java или собственную память.Рекомендуется, чтобы реализация JVM поместила метаданные класса в родную памяти и поместите пул строк и класс в собственную память. Статические переменные класса помещаются в кучу java. Таким образом, сколько метаданных класса может быть загружено, контролируется не MaxPermSize, а фактическое доступное пространство системы.
В соответствии с приведенным выше описанием можно предположить, что исключения в области метода тестирования не появятся в JDK 1.8.
Добро пожаловать, чтобы оставить сообщение, чтобы дополнить и общаться друг с другом. Личный публичный аккаунт WeChat за внимание: