Увидимся поздно, руководство по разделению области памяти виртуальной машины Java

Java задняя часть
Увидимся поздно, руководство по разделению области памяти виртуальной машины Java

Эй, клоун на самом деле я, поиск в WeChat»Тихий король 2"Следуйте за этим тихим, но интересным программистом~

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

Файл исходного кода Java компилируется компилятором для создания файла байт-кода, а затем передается загрузчику классов JVM, после загрузки он передается механизму выполнения для выполнения. В течение всего процесса выполнения JVM будет использовать пространство для хранения данных, необходимых во время выполнения программы.Это пространство обычно называется областью данных времени выполнения, которую часто называют памятью JVM.

Поэтому, когда мы говорим о разделении областей памяти JVM, мы фактически говорим об этом пространстве — области данных времени выполнения.

Каждый должен что-то знать об официальной «Спецификации виртуальной машины Java», верно? Знание этой спецификации может дать нам более глубокое понимание JVM. Спецификация в основном состоит из 6 частей, а именно:

  • Глава 1 Введение
  • Глава 2: Архитектура виртуальной машины Java
  • Глава 3: Компиляция виртуальной машины Java
  • Глава четвертая:Файл класса
  • Глава 5: Загрузка, связывание и инициализация
  • Глава 6: Набор инструкций виртуальной машины Java
  • Глава 7: Коды операций

В соответствии с положениями главы 2 «Структура виртуальной машины Java» область данных времени выполнения можно разделить на следующие части, как показано на рисунке ниже.

01. Счетчик программ

Счетчик программ (регистр счетчика программ) занимает небольшой объем памяти, который можно рассматривать как индикатор номера строки инструкции байт-кода, выполняемой текущим потоком. Интерпретатор байт-кода изменит значение этого счетчика во время работы, чтобы выбрать следующую инструкцию байт-кода для выполнения.Такие функции, как ветвление, цикл, переход, обработка исключений и восстановление потока, должны полагаться на этот счетчик для завершения.

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

То есть мы требуем, чтобы программный счетчик был потоко-приватным.

«Спецификация виртуальной машины Java» предусматривает, что если поток выполняет несобственный метод, программный счетчик сохраняет адрес инструкции, которая должна быть выполнена в данный момент; если поток выполняет собственный метод, значение в программном счетчике равно неопределенный .

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

Так как пространство, занимаемое данными, хранящимися в программном счетчике, не будет изменяться в размере при выполнении программы, программный счетчик не будет иметь явления OutOfMemory.

02. Стек виртуальной машины Java

В стеке виртуальной машины Java есть кадры стека, и каждый кадр стека соответствует вызываемому методу. Когда поток выполняет метод, он создает соответствующий кадр стека и помещает кадр стека в стек. Когда метод завершит выполнение, удалите кадр стека из стека.кучаОн следует принципу «последним пришел — первым вышел», поэтому кадр стека, соответствующий методу, выполняемому в данный момент потоком, должен находиться наверху стека виртуальной машины Java.

Фрейм стека содержит следующие 5 частей, как показано на рисунке ниже.

1) Таблица локальных переменных

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

2) Стек операндов

Вычисление выражений выполняется в стеке операндов. Когда метод начинает выполняться, стек операндов этого метода пуст.Во время выполнения метода будут различные инструкции байт-кода для записи и извлечения содержимого в стек операндов, то есть операции push/stack/Pop. Например, арифметические операции выполняются через стек операндов или параметры передаются через стек операндов при вызове других методов.

3) Ссылка на пул констант времени выполнения

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

4) Адрес возврата метода

После того, как метод выполнен (будь то нормальное выполнение или возникает исключение), ему необходимо вернуться в место, где был вызван метод, прежде чем программа сможет продолжить выполнение.В адресе возврата метода сохраняется некоторая информация, помогающая восстановить состояние выполнения. метода верхнего уровня.

5) Динамическое связывание

Каждый фрейм стека содержит ссылку на метод в пуле констант времени выполнения, к которому принадлежит фрейм стека.Эта ссылка сохраняется для поддержки динамического связывания во время вызова метода.

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

В стеке виртуальной машины Java есть два типа ошибок:

  • StackOverFlowError: вызывается, когда глубина стека запросов потока превышает максимальную глубину стека виртуальной машины Java.
  • OutOfMemoryError: если стек виртуальной машины Java допускает динамическое расширение, он генерируется, когда недостаточно памяти не может быть применено при расширении стека.

Емкость стека самой известной виртуальной машины HotSpot не допускает динамического расширения, поэтому OutOfMemoryError на виртуальной машине HotSpot не будет.

03. Стек локальных методов

Стек собственных методов похож на стек виртуальной машины Java, за исключением того, что стек собственных методов выполняет собственные методы, то есть методы, измененные с помощью ключевого слова native.

В виртуальной машине HotSpot нет различий между стеком собственных методов и стеком виртуальной машины Java.

04. Куча

Куча — это область памяти, совместно используемая всеми потоками, созданная при запуске виртуальной машины Java для хранения объектов (массив также является объектом).

В прошлом «почти» все объекты в Java размещались в куче, но с развитием JIT-компиляторов (Just-In-Time) и постепенной зрелостью технологии побега постепенно стало невозможным размещать все объекты в куче. куча.Тогда "совсем". Начиная с JDK 7, виртуальная машина Java по умолчанию включила escape-анализ, а это значит, что если ссылка на объект в некоторых методах не возвращается или не используется снаружи (то есть не экранируется), то объект может напрямую выделять память в стеке .

Краткое объяснение JIT и анализа побегов.

Распространенные компилируемые языки, такие как C++, обычно компилируют код непосредственно в машинный код, который ЦП может понять для запуска. Для того, чтобы реализовать особенность «скомпилировать один раз, запускать везде», Java делит процесс компиляции на две части: сначала он будет компилироваться в общую промежуточную форму — байт-код javac, а затем байт-код будет компилироваться интерпретатором. один за другим.Интерпретируется как машинный код для выполнения. Таким образом, с точки зрения производительности Java может быть не в состоянии выполнять компилируемые языки, такие как C++.

Чтобы оптимизировать производительность Java, JVM вводит JIT-компилятор в дополнение к интерпретатору: когда программа запускается, интерпретатор вступает в действие первым, и код может выполняться напрямую. Со временем в игру вступают компиляторы «точно в срок», которые компилируют и оптимизируют все больше и больше кода в собственный код для достижения более высокой эффективности выполнения. В настоящее время интерпретатор можно использовать как средство понижения версии компиляции и запуска.При возникновении некоторых ненадежных проблем с оптимизацией компиляции переключитесь обратно на интерпретацию и выполнение, чтобы убедиться, что программа может работать нормально.

Escape Analysis (Анализ побега), проще говоря, виртуальная машина Hotspot может анализировать область использования вновь созданных объектов и решать, следует ли выделять память в куче Java.

Куча — это основная область, управляемая сборщиком мусора Java, поэтому ее также называют кучей GC (Garbage Collected Heap). С точки зрения сборки мусора, поскольку сборщик мусора в основном использует алгоритм сборки мусора поколений, куча также может быть подразделена на: новое поколение и старое поколение. Новое поколение также можно подразделить на: пространство Эдема, Из выжившего, В пространство выжившего и т. д. Целью дальнейшего деления является более эффективное использование памяти или ее более быстрое выделение.

Наиболее подвержена этому ошибка OutOfMemoryError, которая делится на следующие формы:

  • OutOfMemoryError: GC Overhead Limit Exceeded: Эта ошибка возникает, когда JVM тратит слишком много времени на сборку мусора и может освободить только небольшое пространство кучи.
  • java.lang.OutOfMemoryError: Java heap space: эта ошибка возникает, если в памяти кучи недостаточно места для хранения вновь созданного объекта при создании нового объекта. Это не имеет ничего общего с физической памятью машины, это не имеет никакого отношения к размеру памяти виртуальной машины, которую мы настроили!

05. Метапространство

В JDK 8 исходная область метода (точнее, постоянное поколение) была полностью удалена и заменена метапространством.

Давайте поговорим о методической области. Как и куча, область методов — это область, разделяемая потоками, которая используется для хранения информации о классах, констант, статических переменных и скомпилированного кода, загруженного виртуальной машиной Java.

В некоторых местах область метода также называют постоянной генерацией. Но это нельзя понимать так.

«Спецификация виртуальной машины Java» определяет только концепцию области метода и ее функции, но не указывает, как ее реализовать. Тогда разные виртуальные машины Java могут иметь разные реализации. Постоянная генерация — это реализация области метода HotSpot. Другими словами, постоянное поколение — это просто концепция в HotSpot, а область методов — это определение, спецификация в спецификации виртуальной машины Java.

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

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

До JDK 7 пул констант времени выполнения содержал пул строковых констант, все в области методов.

В JDK 7 пул строковых констант был вынесен из области методов и помещен в кучу, а другие элементы пула констант времени выполнения по-прежнему находились в области методов.

В JDK 8 HotSpot удалил постоянное поколение, что означает, что область метода не существует и заменена метапространством. Это означает, что пул строковых констант находится в куче, а пул констант времени выполнения уходит в метапространство.

Давайте поговорим о том, почему вы хотите заменить постоянное поколение (PermGen) или область метода метапространством (MetaSpace).

Во-первых, если постоянное поколение помещается в виртуальную машину Java, оно будет ограничено объемом памяти виртуальной машины Java, а метапространство использует локальную память, которая свободна от ограничения памяти виртуальной машины Java.

Во-вторых, в JDK 8 виртуальная машина JRockit была интегрирована в HotSpot, и в JRockit нет концепции постоянной генерации, поэтому нет необходимости открывать еще одно пространство для нового HotSpot как постоянной генерации.


Нам, программистам Java, не нужно беспокоиться об утечках памяти и переполнении памяти, как программистам C/C++, но в реальной работе частота этих двух проблем довольно высока, особенно в случае многопоточного параллелизма. Если вы не понимаете, как виртуальная машина Java управляет памятью, вы можете растеряться, когда столкнетесь с проблемами.

Понимание разделения области памяти виртуальной машины Java помогает нам лучше понять виртуальную машину Java, чтобы понять инициативу по устранению неполадок с памятью.

Я второй брат, который эволюционировал в тишине, надеюсь, все смогут тайком вырасти как я, а потом всех поразить. Увидимся в следующий раз~

Я всегда буду настаивать на создании, если вы найдете контент полезным, добро пожаловать в поиск WeChat "Тихий король 2"Первый раз читаю, отвечаю"102«Есть чисто Java-версия заметок по чистке LeetCode, которые я недавно скомпилировал, с полными 300 решениями проблем.

P.S. Сегодня получил подарок от Наггетс. Чувствую себя комфортно. Чувствую, что Наггетс — это творческая площадка!