Структура памяти JVM

JVM
Структура памяти JVM

JVM ≠ Человек из японского видео

Основная причина для написания этого — написать «знаком с базовой структурой JVM» в резюме, а другая причина — позволить всем, кто читает мою статью, также написать это предложение, он действительно полезный и красивый парень. . . . Ну, не только для обучения на собеседовании, но, что более важно, для создания собственной системы знаний JVM.Javaers должен иметь широкий стек технологий, но их JVM должен быть глубоким.

Нравится + Избранное, чтобы узнать серию, статьи включены в GitHubJavaKeeper, N-line интернет-разработка, спектр основных навыков оружия, делайте заметки самостоятельно

прямое интервью

Я продолжал читать с этими вопросами в любом случае.

  • Давайте поговорим об области данных среды выполнения JVM. Что это за области? Что они делают?
  • Улучшения генерации памяти в Java 8
  • Пример переполнения стека?
  • Регулировка размера стека, можно ли его сохранить без переполнения?
  • Чем больше выделенная память стека, тем лучше?
  • Будет ли при сборке мусора задействован стек виртуальной машины?
  • Являются ли локальные переменные определенными в методах потокобезопасными?

область данных времени выполнения

Память является очень важным системным ресурсом, промежуточным хранилищем и мостом между жестким диском и ЦП и обеспечивает работу операционной системы и приложений в режиме реального времени. Схема памяти JVM определяет стратегию использования, распределения и управления памятью Java во время выполнения процесса, что обеспечивает эффективную и стабильную работу JVM. Различные JVM имеют некоторые отличия в способе разделения памяти и механизме управления.

На следующем рисунке показана общая архитектура JVM, а средняя часть — это различные области данных времени выполнения, определенные виртуальной машиной Java.

jvm-framework

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

  • Частный поток: счетчик программ, стек, локальный стек
  • Совместное использование потоков: куча, память вне кучи (постоянная генерация или метапространство, кеш кода)

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

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

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

Здесь вместо физического регистра в широком смысле уместнее называть счетчик программ (или счетчик ПК, или счетчик команд), и вызвать какие-то ненужные недоразумения непросто.Регистры ПК в JVM являются абстрактным аналогом физических регистров ПК..

Счетчик программ — это небольшое пространство памяти, которое можно рассматривать как байт-код, выполняемый текущим потоком.индикатор номера строки.

1.1 Функция

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

jvm-pc-counter

(Анализ: Войдите в каталог, где находится файл класса, выполнитеjavap -v xx.classАнти-парс (или через плагин IDEAJclasslibПосмотреть непосредственно выше), вы можете увидеть область кода (инструкцию по сборке), таблицу локальных переменных, таблицу исключений, таблицу отображения смещения строки кода, постоянный пул и другую информацию, соответствующую текущему классу. )

1.2 Обзор

  • Это крошечный кусочек памяти, почти незаметный. Также самая быстрая область хранения
  • В спецификации JVM каждый поток имеет свой собственный программный счетчик, который является частным для потока и имеет тот же жизненный цикл, что и жизненный цикл потока.
  • В каждый момент времени в потоке выполняется только один метод, так называемыйтекущий метод. Если текущий поток выполняет метод Java, программный счетчик записывает адрес инструкции байт-кода JVM, а если он выполняет собственный метод, это неопределенное значение (undefined).
  • Это индикатор потока управления программой. Основные функции, такие как ветвление, циклы, переходы, обработка исключений и восстановление потока, должны полагаться на этот счетчик для выполнения.
  • Когда интерпретатор байт-кода работает, он выбирает следующую инструкцию байт-кода для выполнения, изменяя значение этого счетчика.
  • Это единственный, который не указывает ничего в спецификации JVM.OutOfMemoryErrorрайон ситуации

👨‍💻: Какая польза от использования регистров ПК для хранения адресов инструкций байт-кода? Зачем использовать регистр ПК для записи адреса выполнения текущего потока?

🙋‍♂️: Поскольку процессору необходимо постоянно переключать потоки, после переключения обратно вы должны знать, с чего начать выполнение. Интерпретатор байт-кода JVM должен изменить значение регистра PC, чтобы определить, какая инструкция байт-кода должна быть выполнена следующей.

👨‍💻: Почему регистр ПК настроен как частный поток?

🙋‍♂️: Многопоточность будет выполнять только один из методов потока в определенный период времени, а ЦП будет постоянно переключать задачи, что неизбежно приведет к частым прерываниям или восстановлению. Чтобы точно записать текущий адрес инструкции байт-кода, выполняемой каждым потоком, каждому потоку выделяется регистр ПК, и каждый поток вычисляет независимо и не влияет друг на друга.


Во-вторых, стек виртуальной машины

2.1 Обзор

Стеки виртуальных машин Java (стеки виртуальных машин Java), также называемые стеками Java в первые дни. Когда создается каждый поток, он создает стек виртуальной машины, в котором хранится один за другим кадр стека (Stack Frame), который соответствует многократному вызову метода Java, является частным для потока и имеет тот же жизненный цикл, что и нить.

Роль: отвечает за выполнение программы Java, сохраняет локальные переменные и частичные результаты метода, а также участвует в вызове и возврате метода.

Функции:

  • Стек — это быстрый и эффективный способ распределения памяти, а его скорость доступа уступает только счетчику программ.
  • У JVM есть только две операции непосредственно в стеке виртуальной машины: выполняется каждый метод, сопровождаемыйвставить в стек(Push/Push), выполнение метода завершаетсяпоп
  • В стеке нет проблем со сборкой мусора

Возможные исключения в стеке:

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

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

через параметр-XssЧтобы установить максимальное пространство стека потока, размер стека напрямую определяет максимально достижимую глубину вызова функции.

Официально предоставленные эталонные инструменты, вы можете проверить некоторые параметры и операции:docs.Oracle.com/java-color/8/do…

2.2 Единица хранения стека

Что хранится в стеке?

  • Каждый поток имеет свой собственный стек, и данные в стекеФормат кадра стека (Stack Frame) существует
  • Каждый метод, выполняемый в этом потоке, имеет свой собственный соответствующий кадр стека.
  • Кадр стека — это блок памяти и набор данных, который поддерживает различную информацию о данных во время выполнения метода.

2.3 Принцип работы стека

  • JVM имеет только две операции непосредственно в стеке Java, одна в кадре стека.толкать стекипоп, следовать принципу "первым пришел, последним вышел/последним пришел, первым ушел"

  • В активном потоке в определенный момент времени есть только один активный кадр стека. то есть только кадр стека текущего исполняемого метода (верхняя рама стека) действителен, этот кадр стека называетсятекущий кадр стека(Текущий кадр), метод, соответствующий текущему кадру стека,текущий метод(текущий метод), класс, который определяет этот метод,текущий класс(текущий класс)

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

  • Если в этом методе вызываются другие методы, соответствующий новый кадр стека будет создан и помещен на вершину стека, которая называется новым текущим кадром стека.

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

  • Если текущий метод вызывает другие методы, при возврате метода текущий фрейм стека вернет результат выполнения этого метода в предыдущий фрейм стека.Затем виртуальная машина отбрасывает текущий фрейм стека, делая предыдущий фрейм стека текущим стек кадр снова.

  • Методы Java имеют два способа возврата функций:Один из них — обычный возврат функции с использованием инструкции возврата, а другой — исключение.В любом случае фрейм стека будет вытолкнут.

Во время отладки IDEA в окне отладки можно увидеть наложение и извлечение различных методов во фреймах.

2.4 Внутренняя структура рамы стека

каждыйкадр стека(Stack Frame) хранит:

  • Локальные переменные
  • Стек операндов (или стек выражений)
  • Динамическое связывание: ссылка на метод для пула констант времени выполнения.
  • Адрес возврата метода (Return Address): адрес, по которому метод завершается нормально или ненормально.
  • некоторая дополнительная информация

jvm-stack-frame

Продолжайте бросать пять частей в кадр стека~~

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

  • Таблица локальных переменных также известна как массив локальных переменных или таблица локальных переменных.
  • представляет собой набор пространства для хранения переменных значений,В основном используется для хранения параметров метода и локальных переменных, определенных в теле метода., включая различные виртуальные машины Java, известные компиляторубазовый тип данных(логическое, байтовое, символьное, короткое, целое, плавающее, длинное, двойное),ссылка на объект(ссылочный тип, который не эквивалентен самому объекту, может быть указателем ссылки на начальный адрес объекта или может указывать на дескриптор, представляющий объект или другие связанные местоположения) иreturnAddressТип (указывает на адрес инструкции байт-кода, замененной таблицей исключений)
  • Поскольку таблица локальных переменных построена на стеке потока, это частные данные потока, поэтомуНет проблем с безопасностью данных
  • Требуемая емкость таблицы локальных переменных определяется во время компиляции.и сохраните его в свойстве Code методаmaximum local variablesв элементе данных. Размер таблицы локальных переменных не изменяется во время выполнения метода.
  • Количество вложенных вызовов методов определяется размером стека. Вообще говоря,Чем больше стек, тем больше вызовов вложенных методов.. Для функции, чем больше у нее параметров и локальных переменных, тем больше таблица локальных переменных и больше ее кадр стека, чтобы удовлетворить потребности в увеличении объема информации, которая должна передаваться при вызовах методов. В свою очередь, вызовы функций будут занимать больше места в стеке, что приведет к меньшему количеству вложенных вызовов.
  • Переменные в таблице локальных переменных действительны только в текущем вызове метода.. Когда метод выполняется, виртуальная машина завершает процесс передачи значения параметра в список переменных параметров, используя таблицу локальных переменных. Когда вызов метода завершится, вместе с уничтожением фрейма стека методов будет уничтожена и таблица локальных переменных.
  • Значение параметра всегда хранится по индексу 0 массива локальных переменных и заканчивается на индексе длины массива -1
Слот
  • Самая основная единица хранения таблицы локальных переменных — Slot (переменный слот).

  • В таблице локальных переменных типы в пределах 32 бит занимают только один слот (включая тип returnAddress), а 64-битные типы (long и double) занимают два последовательных слота.

    • byte, short, char преобразуются в int перед сохранением, boolean также преобразуется в int, 0 означает false, не-0 означает true
    • long и double занимают два слота
  • JVM назначит индекс доступа для каждого слота в таблице локальных переменных, через который можно успешно получить доступ к значению локальной переменной, указанному в таблице локальных переменных.Значение индекса находится в диапазоне от 0 до максимального количества слотов в таблице локальных переменных. .

  • Когда вызывается метод экземпляра, его параметры метода и локальные переменные, определенные внутри тела метода, будуткопируются по порядкук каждому слоту в таблице локальных переменных

  • Если вам нужно получить доступ к 64-битному значению локальной переменной в таблице локальных переменных, вам нужно использовать только предыдущий индекс. (Например: при доступе к переменным типа long или double не разрешается каким-либо образом обращаться к одному из слотов по отдельности)

  • Если текущий кадр создается конструктором или методом экземпляра, то ссылка на объект this будет храниться в Slot с индексом 0, а остальные параметры будут продолжать располагаться в порядке списка параметров (здесь вопрос: почему не в статическом методе?На это можно сослаться, потому что эта переменная не существует в таблице локальных переменных текущего метода)

  • Слоты в таблице локальных переменных в кадре стека можно использовать повторно., если локальная переменная превышает свою область действия, новая локальная переменная, объявленная после ее области действия, скорее всего, повторно использует слот локальной переменной с истекшим сроком действия, таким образомдостичь цели экономии ресурсов. (На рисунке ниже this, a, b и c теоретически должны иметь 4 переменные, а c повторно использует слот b)

  • Во фрейме стека наиболее тесно связанной с настройкой производительности является таблица локальных переменных. Во время выполнения метода виртуальная машина использует таблицу локальных переменных для завершения передачи метода.
  • Переменные в таблице локальных переменных также являются важными корневыми узлами сборки мусора, если объекты, на которые прямо или косвенно ссылаются в таблице локальных переменных, не будут переработаны.

2.4.2 Стек операндов

  • В дополнение к таблице локальных переменных каждый независимый кадр стека также содержитпоследний пришел первый вышелСтек операндов (Last-In-First-Out), также известный какстек выражений(стек выражений)

  • Стек операндов, в процессе выполнения метода, согласно инструкциям байткода, данные записываются или извлекаются из стека операндов, то есть push и pop.

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

Обзор
  • стек операндов,Он в основном используется для сохранения промежуточных результатов процесса расчета, а также используется в качестве временного хранилища для переменных в процессе расчета.
  • Стек операндов — это рабочая область исполняющего движка JVM, когда метод начинает выполняться, также создается новый кадр стека.На данный момент стек операндов этого метода пуст.
  • Каждый стек операндов будет иметь определенную глубину стека для хранения значений.Требуемая максимальная глубина определяется во время компиляции и сохраняется в атрибуте кода метода.max_stackв элементе данных
  • Любой элемент в стеке может иметь любой тип данных Java.
    • 32-битные типы занимают глубину единицы стека
    • 64-битные типы занимают две единицы стека в глубину.
  • Стек операндов не использует индекс доступа для доступа к данным, а может выполнять доступ к данным только посредством стандартных операций push и pop.
  • Если вызываемый метод имеет возвращаемое значение, возвращаемое значение будет помещено в стек операндов текущего кадра стека., и обновить следующую инструкцию байт-кода, которая должна быть выполнена в регистре ПК
  • Типы данных элементов в стеке операндов должны точно соответствовать последовательности инструкций байт-кода, которая проверяется компилятором во время компиляции и снова на этапе анализа потока данных этапа проверки класса процесса загрузки класса.
  • Также мы говоримМеханизм интерпретации виртуальной машины Java представляет собой механизм выполнения на основе стека., где стек относится к стеку операндов
Кэширование поверх стека

Исполнительный механизм HotSpot не использует архитектуру на основе регистров, но это не означает, что реализация HotSpot VM косвенно не использует ресурсы регистров. Регистр является одним из компонентов физического ЦП, а также очень важным высокоскоростным ресурсом хранения в ЦП. Вообще говоря, скорость чтения/записи регистров очень высока, даже в десятки раз выше скорости чтения/записи памяти, но ресурсы регистров очень ограничены, а количество регистров ЦП на разных платформах различно и неравномерно. Регистры в основном используются для кэширования данных, таких как локальные машинные инструкции, значения и адрес следующей выполняемой инструкции.

Инструкции без адреса, используемые виртуальной машиной на основе стека, более компактны, но для завершения операции необходимо использовать больше инструкций push и pop, что также означает, что потребуется больше диспетчеризации инструкций (диспетчеризация инструкций) и чтение/запись памяти. раз. Поскольку операнды хранятся в памяти, частые операции чтения/записи памяти неизбежно влияют на скорость выполнения. Чтобы решить эту проблему, разработчики HotSpot JVM предложили технологию кэширования поверх стека,Кэшируйте все верхние элементы стека в регистрах физического ЦП, чтобы уменьшить количество операций чтения/записи в память и повысить эффективность выполнения механизма выполнения.

2.4.3 Динамическое связывание (методы ссылаются на пулы констант времени выполнения)

  • Каждый кадр стека содержит ссылку на метод в пуле констант времени выполнения, к которому принадлежит кадр стека.. Целью включения этой ссылки является поддержка кода текущего метода для достижения динамической компоновки (Dynamic Linking).
  • Когда исходный файл Java компилируется в файл байт-кода, все ссылки на переменные и методыСимволическая ссылка(Символическая ссылка) хранится в пуле констант файла класса. Например: когда метод вызывает другой метод, он представлен символической ссылкой на метод в пуле констант, затемРоль динамической компоновки заключается в преобразовании этих символических ссылок в прямые ссылки на вызывающий метод.

jvm-dynamic-linking

Как JVM выполняет вызовы методов

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

[Эта часть контента, помимо вызовов методов, также включает синтаксический анализ и диспетчеризацию (статическую отправку, динамическую отправку, одиночную отправку и множественную отправку), которые не будут здесь представлены и будут раскопаны позже.]

В JVM преобразование символической ссылки в прямую ссылку на вызывающий метод связано с механизмом привязки метода.

  • статическая ссылка: Когда файл байт-кода загружается в JVM, если вызываемыйЦелевой метод известен во время компиляции, а время выполнения остается неизменным. Процесс преобразования символической ссылки вызывающего метода в прямую ссылку в этом случае называется статическим связыванием.
  • динамическая ссылка: Если вызываемый метод не может быть определен во время компиляции, то есть символическая ссылка вызывающего метода может быть преобразована в прямую ссылку только во время выполнения программы.Поскольку этот процесс преобразования ссылки является динамическим, он также называется динамической ссылкой.

Механизм связывания соответствующего метода: Раннее связывание (Early Binding) и Позднее связывание (Late Binding).Связывание — это процесс, при котором символические ссылки на поле, метод или класс заменяются прямыми ссылками, что происходит только один раз..

  • Раннее связывание:Раннее связывание означает, что вызываемый целевой метод известен во время компиляции и остается неизменным во время выполнения., вы можете привязать этот метод к типу, к которому он принадлежит.Таким образом, поскольку ясно, какой целевой метод вызывается, вы можете использовать статическую компоновку для преобразования символических ссылок в прямые ссылки.
  • Позднее связывание: если вызываемый метод не может быть определен компилятором, а связанный метод может быть связан только в соответствии с фактическим типом во время выполнения программы, этот метод связывания называется поздним связыванием.
виртуальные и невиртуальные методы
  • Если метод имеет конкретную вызывающую версию, определенную компилятором, эта версия неизменяема во время выполнения. Такие методы называются невиртуальными методами, например, статические методы, частные методы, окончательные методы, конструкторы экземпляров и методы родительского класса — все это невиртуальные методы.
  • Другие методы называются виртуальными методами.
таблица виртуальных методов

В объектно-ориентированном программировании часто используется динамическая диспетчеризация.Если для каждой динамической диспетчеризации требуется повторный поиск соответствующей цели в метаданных метода класса, это может повлиять на эффективность выполнения. Чтобы повысить производительность, JVM использует виртуальную таблицу методов в области методов класса и использует индексную таблицу вместо поиска. Невиртуальные методы не отображаются в таблице.

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

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

2.4.4 Адрес возврата метода (обратный адрес)

Используется для хранения значения регистра ПК, вызвавшего метод.

Конец метода, есть два пути

  • нормальное выполнение завершено
  • Произошло необработанное исключение, аварийный выход

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

Когда метод начинает выполняться, есть только два способа выйти из метода:

  1. Когда механизм выполнения встречает инструкцию байт-кода, возвращенную любым методом, возвращаемое значение будет передано вышестоящему вызывающему методу, сокращенноЗавершите экспорт в обычном режиме

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

    Среди инструкций байт-кода инструкции возврата включают ireturn (используется, когда возвращаемое значение имеет тип boolean, byte, char, short и int), lreturn, freturn, dreturn и areturn, а также есть инструкция возврата для методов, объявленных как void. , используются методы инициализации экземпляра, методы инициализации класса и интерфейса.

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

    Обработка исключений при возникновении исключения во время выполнения метода сохраняется в таблице обработки исключений, чтобы было удобно найти код для обработки исключения при возникновении исключения.

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

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

2.4.5 Дополнительная информация

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


В-третьих, локальный стек методов

3.1 Интерфейс собственного метода

Проще говоря, собственный метод — это интерфейс для Java для вызова кода, отличного от Java. Известный нам класс Unsafe имеет множество нативных методов.

Зачем использовать нативный метод?

Java очень удобна в использовании, но некоторые задачи не так просто реализовать на Java, или когда мы очень обеспокоены эффективностью программы, возникает проблема

  • Взаимодействие вне среды Java. Иногда приложению Java необходимо взаимодействовать со средой вне среды Java, поэтому существуют собственные методы.
  • Взаимодействие с операционной системой: JVM поддерживает сам язык Java и библиотеки времени выполнения, но иногда все же необходимо полагаться на некоторую базовую системную поддержку. С помощью нативных методов мы можем использовать Java для взаимодействия с базовой системой, реализующей jre, а некоторые части JVM написаны на языке C.
  • Java от Sun: интерпретатор Sun реализован на C, что позволяет ему взаимодействовать с внешним миром, как обычный C. Большая часть jre реализована на Java, а также взаимодействует с внешним миром через некоторые нативные методы. Например, классjava.lang.ThreadизsetPriority()метод реализован на Java, но в нем реализованы вызовы нативных методов классаsetPrioruty(), который реализован на C и встроен в JVM.

3.2 Стек собственных методов

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

  • Стеки нативных методов также являются потокозависимыми.

  • Разрешить потоку фиксированный или динамически расширяемый размер памяти

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

  • Его специфический подход заключается вMative Method Stackзарегистрировать нативный метод вExecution EngineЗагрузка библиотек собственных методов во время выполнения Когда поток вызывает собственный метод, он попадает в совершенно новый мир, который больше не ограничен виртуальной машиной. Он имеет те же права, что и виртуальная машина.

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

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

  • В Hotspot JVM локальный локальный стек и стек виртуальной машины напрямую объединены в один.


Стек — это единица времени выполнения, а куча — единица хранения..

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

В-четвертых, куча памяти

4.1 Разделение памяти

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

Для эффективной сборки мусора виртуальная машина хранит память в куче.логическиразделена на три области (Единственная причина генерации — оптимизация производительности GC.):

  • Новое поколение (молодое поколение): новые предметы и предметы, не достигшие определенного возраста, находятся в новом поколении
  • Старое поколение (пенсионная область): для объектов, которые используются в течение длительного времени, пространство памяти старого поколения должно быть больше, чем у молодого поколения.
  • Метапространство (называемое постоянным поколением до JDK1.8): подобно работе с временными объектами в некоторых методах, до JDK1.8 память JVM была занята, а после JDK1.8 напрямую использовалась физическая память.

JDK7

Спецификация виртуальной машины Java предусматривает, что куча Java может находиться в физически прерывистом пространстве памяти, если она логически непрерывна, как и дисковое пространство. При реализации он может быть либо фиксированного размера, либо расширяемым, а основные виртуальные машины расширяемы (через-Xmxи-Xmscontrol), если в куче не было завершено выделение экземпляра и куча больше не может быть расширена, она выбрасываетсяOutOfMemoryErrorаномальный.

Молодое поколение

Молодое поколение – это место, где создаются все новые объекты. Когда молодое поколение заполнено, выполняется сборка мусора. Эта сборка мусора называетсяMinor GC. Молодое поколение разделено на три части - Эдемский сад (Eden Memory) и две зоны выживания (Survivor Memory, известный как от/до или s0/s1), масштаб по умолчанию8:1:1

  • Большинство вновь созданных объектов находится в пространстве памяти Эдема.
  • Когда пространство Эдема заполнится объектами, выполнитеMinor GC, и переместите все выжившие объекты в пространство для выживших
  • Minor GC проверяет объекты выживших и перемещает их в другое пространство выживших. Так что каждый раз место выжившего всегда пусто
  • Объекты, пережившие несколько циклов сборки мусора, перемещаются в старое поколение. Обычно это достигается путем установки возрастного порога для объектов молодого поколения, прежде чем они получат право на повышение до старшего поколения.

Старое поколение

Память старого поколения содержит объекты, пережившие множество циклов небольших сборщиков мусора. Обычно сборка мусора выполняется, когда память старого поколения заполнена. Сборка мусора старого поколения называется Major GC и обычно занимает больше времени.

Большие объекты уходят сразу в старость (большие объекты — это объекты, требующие много непрерывного пространства памяти). Цель этого состоит в том, чтобы избежать большого количества копий памяти между областью Эдема и двумя областями Выживших.

метапространство

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

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

Поэтому метапространство помещается в область метода сзади.

4.2 Установка размера динамической памяти и OOM

Куча Java используется для хранения экземпляров объектов Java, поэтому размер кучи определяется при запуске JVM.-Xmxи-Xmsустанавливать

  • -XmxИспользуется для представления начальной памяти кучи, эквивалентной-XX:InitialHeapSize
  • -XmsИспользуется для представления максимальной памяти кучи, эквивалентной-XX:MaxHeapSize

Если размер памяти кучи превышает-XmsПоставь максимальную память, выкинетOutOfMemoryErrorаномальный.

мы обычно-Xmxи-XmsДва параметра настроены с одинаковыми значениями. Цель — повысить производительность за счет устранения необходимости повторного разделения размера кучи вычислений после того, как механизм сборки мусора очистит область кучи.

  • По умолчанию начальный размер кучи составляет: размер памяти компьютера/64.

  • По умолчанию максимальный размер кучи составляет: объем памяти компьютера/4.

Мы можем получить значения наших настроек через код, и, конечно же, мы можем смоделировать OOM:

public static void main(String[] args) {

  //返回 JVM 堆大小
  long initalMemory = Runtime.getRuntime().totalMemory() / 1024 /1024;
  //返回 JVM 堆的最大内存
  long maxMemory = Runtime.getRuntime().maxMemory() / 1024 /1024;

  System.out.println("-Xms : "+initalMemory + "M");
  System.out.println("-Xmx : "+maxMemory + "M");

  System.out.println("系统内存大小:" + initalMemory * 64 / 1024 + "G");
  System.out.println("系统内存大小:" + maxMemory * 4 / 1024 + "G");
}

Просмотр распределения памяти кучи JVM

  1. В случае, когда размер памяти кучи JVM не настроен по умолчанию, JVM настраивает текущий размер памяти в соответствии со значением по умолчанию.

  2. По умолчанию соотношение молодого поколения к старому составляет 1:2.–XX:NewRatioнастроить

    • в новом поколенииEden:From Survivor:To SurvivorСоотношение8:1:1, в состоянии пройти-XX:SurvivorRatioнастроить
  3. Если включено в JDK 7-XX:+UseAdaptiveSizePolicy, JVM будет динамически регулировать размер каждой области в куче JVM и возраст входа в старое поколение

    В настоящее время–XX:NewRatioи-XX:SurvivorRatioне получится, а JDK 8 включен по умолчанию-XX:+UseAdaptiveSizePolicy

    В JDK 8,Не закрывайте произвольно-XX:+UseAdaptiveSizePolicy, если только нет четкого плана разделения кучи памяти

Размеры Эдема, От Выжившего до Выжившего будут пересчитываться после каждого GC

Расчет основан напроцесс GCстатистикавремя GC,пропускная способность,объем памяти

java -XX:+PrintFlagsFinal -version | grep HeapSize
    uintx ErgoHeapSizeLimit                         = 0                                   {product}
    uintx HeapSizePerGCThread                       = 87241520                            {product}
    uintx InitialHeapSize                          := 134217728                           {product}
    uintx LargePageHeapSizeThreshold                = 134217728                           {product}
    uintx MaxHeapSize                              := 2147483648                          {product}
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)
$ jmap -heap 进程号

4.3 Жизненный цикл объектов в куче

  1. В куче модели памяти JVM куча делится на молодое поколение и старое поколение.
    • Новое поколение делится наЭдемский райониОбласть выжившего, область Survivor состоит изFrom SurvivorиTo Survivorсочинение
  2. При создании объекта объект будет преимущественно размещен в районе Эдема нового поколения.
    • На этом этапе JVM определитобъект молодой счетчик(-XX:MaxTenuringThreshold)
  3. Когда места в Eden недостаточно, JVM выполнит сборку мусора молодого поколения (Minor GC).
    • JVM перенесет уцелевшие объекты в Survivor, а возраст объекта +1
    • Объекты также проходят Minor GC в Survivor.Каждый раз, когда они проходят Minor GC, возраст объекта увеличивается на 1.
  4. Если выделенный объект превышает-XX:PetenureSizeThreshold, объект будетнепосредственно отнесены к старому поколению

4.4 Процесс размещения объектов

Выделение памяти под объекты — очень кропотливая и сложная задача, разработчикам JVM нужно не только учитывать, как и где выделяется память, но и учитывать выполнение GC, поскольку алгоритмы выделения памяти тесно связаны с алгоритмами восстановления памяти. фрагментация в пространстве памяти после восстановления памяти.

  1. Новый объект сначала помещается в область Эдема, размер которой ограничен.
  2. Когда пространство Эдемского сада заполнено, программе необходимо снова создать объекты.Сборщик мусора JVM выполнит сборку мусора (Minor GC) в Эдемском саду и уничтожит объекты в Эдемском саду, которые больше не ссылается на другие объекты. Затем загрузите новый объект и поместите его в область Эдема.
  3. Затем переместите оставшиеся объекты в Эдемском саду в зону выживания 0.
  4. Если сборка мусора будет запущена снова, в это время последний оставшийся в живых будет помещен в область оставшихся в живых 0. Если он не будет переработан, он будет помещен в область оставшихся в живых 1.
  5. Если он снова подвергнется сбору мусора, он будет помещен обратно в область выживания 0 в это время, а затем перейдет в область выживания 1.
  6. Когда я поеду в дом престарелых? По умолчанию установлено 15 меток повторного использования.
  7. В районе выхода на пенсию относительно неторопливо. Когда памяти области изъятия недостаточно, снова запускается Major GC, чтобы очистить память области изъятия.
  8. Если область изъятия выполняет основной сборщик мусора и обнаруживает, что объект не может быть сохранен, возникает исключение OOM.

4.5 Введение в сборку мусора GC

Малый общий сбор, Основной общий сбор, Полный общий сбор

Когда JVM выполняет GC, она не всегда освобождает память кучи (новое поколение, старое поколение, область методов) вместе, в большинстве случаев это относится к новому поколению.

Для реализации HotSpot VM сборщик мусора в нем разделен на две категории по области восстановления: частичный сбор (Partial GC) и полный сбор кучи (Full GC).

  • Частичная сборка: сборка мусора, которая не полностью собирает всю кучу Java. Далее он делится на:
    • Коллекция молодого поколения (Minor GC/Young GC): просто сборка мусора молодого поколения
    • Коллекция старого поколения (Major GC/Old GC): просто сборка мусора старого поколения
      • В настоящее время отдельно собирать старое поколение будут только CMS GC.
      • Во многих случаях основной сборщик мусора будет смешиваться с полным сборщиком мусора, и необходимо различать, является ли это сбором старых данных или сборкой всей кучи.
    • Смешанная сборка (Mixed GC): собирает все молодое поколение и некоторую сборку мусора старого поколения.
      • В настоящее время только G1 GC имеет такое поведение
  • Полная сборка кучи (полный сборщик мусора): собирает мусор из всей кучи Java и области методов.

4.6 TLAB

Что такое TLAB (локальный буфер распределения потоков)?

  • С точки зрения модели памяти, а не сборки мусора, область Eden по-прежнему разделена, и JVM выделяет для каждого потока приватную область кеша, которая включается в пространство Eden.
  • Когда несколько потоков выделяют память одновременно, использование TLAB может избежать ряда проблем, не связанных с безопасностью потоков, и в то же время может повысить пропускную способность выделения памяти, поэтому мы можем назвать этот метод выделения памяти какстратегия быстрого размещения
  • Большинство JVM, производных от OpenJDK, предоставляют проекты TLAB.

Зачем нужны TLAB?

  • Область кучи совместно используется потоками, и любой поток может получить доступ к общим данным в области кучи.
  • Поскольку создание экземпляров объектов в JVM происходит очень часто, разделение пространства памяти и области кучи в параллельной среде небезопасно для потоков.
  • Чтобы избежать нескольких потоков, работающих с одним и тем же адресом, необходимо использовать такие механизмы, как блокировка, что, в свою очередь, влияет на скорость выделения.

Хотя не все экземпляры объектов могут успешно выделять память в TLAB, JVM предпочитает TLAB для выделения памяти.

В программе можно пройти-XX:UseTLABУкажите, следует ли включить пространство TLAB.

По умолчанию память пространства TLAB очень мала, занимая всего 1% от всего пространства Eden, мы можем передать-XX:TLABWasteTargetPercentЗадает размер пространства Eden в процентах, занимаемого пространством TLAB.

Если объекту не удается выделить память в пространстве TLAB, JVM попытается выделить память непосредственно в пространстве Eden, используя механизм блокировки для обеспечения атомарности операций с данными.

4.7 Является ли куча единственным вариантом размещения объектного хранилища?

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

анализ побега

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

Основное поведение escape-анализа заключается в анализе динамической области видимости объектов:

  • Когда объект определен в методе и используется только внутри метода, считается, что выхода не произошло.
  • Экранирование происходит, когда на объект ссылается внешний метод после того, как он был определен в методе. Например, он передается как параметр вызова в другие места, что называется побегом метода.

Например:

public static StringBuffer craeteStringBuffer(String s1, String s2) {
   StringBuffer sb = new StringBuffer();
   sb.append(s1);
   sb.append(s2);
   return sb;
}

StringBuffer sbЭто внутренняя переменная метода.В приведенном выше коде sb возвращается напрямую, так что этот StringBuffer может быть изменен другими методами, так что его область видимости находится не только внутри метода, хотя это и локальная переменная, говорят, выйти за пределы метода. К нему могут обращаться даже внешние потоки, например присваивание переменным класса или переменным экземпляра, к которым можно получить доступ в других потоках, что называется выходом из потока.

Приведенный выше код, если вы хотитеStringBuffer sbНе выходя из метода, вы можете написать:

public static String createStringBuffer(String s1, String s2) {
   StringBuffer sb = new StringBuffer();
   sb.append(s1);
   sb.append(s2);
   return sb.toString();
}

Не возвращайте StringBuffer напрямую, тогда StringBuffer не ускользнет от метода.

настройки параметров:

  • После версии JDK 6u23 анализ выхода был включен по умолчанию в HotSpot.
  • Если вы используете более раннюю версию, вы можете пройти-XX"+DoEscapeAnalysisЯвно включено

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

Используя escape-анализ, компилятор может оптимизировать код:

  • выделение стека: преобразование выделения кучи в выделение стека. Если объект выделяется в подпрограмме, чтобы указатель на объект никогда не исчезал, объект может быть кандидатом на выделение стека, а не выделение кучи.
  • синхронизировать пропустить: Если объект оказывается доступным только из одного потока, то операции над этим объектом могут выполняться без оглядки на синхронизацию
  • Отдельный объект или скалярная замена: некоторые объекты могут не существовать как непрерывная структура памяти, и к ним можно получить доступ, тогда часть (или весь) объекта может храниться не в памяти, а в регистрах ЦП.

По результатам escape-анализа во время компиляции JIT-компилятор считает, что если у объекта нет escape-метода, он может быть оптимизирован для размещения в стеке. После завершения выделения он продолжает выполняться в стеке вызовов, и, наконец, поток завершается, пространство стека освобождается, а также восстанавливаются объекты локальных переменных. Это избавляет от необходимости собирать мусор.

Общие сценарии выделения стека: назначение переменных-членов, возвращаемое значение метода, передача ссылки на экземпляр

Синхронный пропуск (устранение) оптимизации кода
  • Стоимость синхронизации потоков довольно высока, и следствием синхронизации является снижение параллелизма и производительности.
  • При динамической компиляции синхронизированных блоков JIT-компилятор может использовать escape-анализ, чтобы определить, может ли объект блокировки, используемый синхронизированным блоком, быть доступен одному потоку без его освобождения другими потоками. Если нет, то JIT-компилятор десинхронизирует код при компиляции синхронизированного блока. Это может значительно улучшить параллелизм и производительность. Этот процесс десинхронизации называетсяИсключение синхронизации, также известное как устранение блокировки.
public void keep() {
  Object keeper = new Object();
  synchronized(keeper) {
    System.out.println(keeper);
  }
}

Как и в приведенном выше коде, объект-хранитель заблокирован в коде, но жизненный цикл объекта-хранителя находится только вkeep()В методе к нему не будут обращаться другие потоки, поэтому он будет оптимизирован на этапе JIT-компиляции. оптимизирован для:

public void keep() {
  Object keeper = new Object();
  System.out.println(keeper);
}
Скалярная замена для оптимизации кода

скаляр(Скаляр) относится к части данных, которую больше нельзя разбить на более мелкие части. Примитивные типы данных в Java являются скалярами.

Напротив, те данные, которые также могут быть разложены, называютсяСумма агрегации(Агрегат), объект в Java является агрегатом, потому что его также можно разложить на другие агрегаты и скаляры.

На этапе JIT, когда с помощью escape-анализа определяется, что к объекту не будет доступа извне, и объект может быть подвергнут дальнейшей декомпозиции, JVM не будет создавать объект, а разложит переменные-члены объекта на несколько переменных-членов, используемых этот метод. . Эти замещенные переменные-члены выделяют место в кадре стека или в регистре. Этот процессскалярная замена.

пройти через-XX:+EliminateAllocationsможно включить скалярную замену,-XX:+PrintEliminateAllocationsПроверьте скалярную замену.

public static void main(String[] args) {
   alloc();
}

private static void alloc() {
   Point point = new Point(1,2);
   System.out.println("point.x="+point.x+"; point.y="+point.y);
}
class Point{
    private int x;
    private int y;
}

В приведенном выше коде точечный объект не экранируется.alloc()метод, а точечные объекты могут быть разложены на скаляры. Тогда JIT не будет напрямую создавать объект Point, а напрямую будет использовать два скаляра int x , int y для замены объекта Point.

private static void alloc() {
   int x = 1;
   int y = 2;
   System.out.println("point.x="+x+"; point.y="+y);
}
Оптимизация кода при распределении стека

Через выделение памяти JVM мы можем знать, что объекты в JAVA выделены в куче. Когда на объекты нет ссылок, нам нужно полагаться на GC для восстановления памяти. Если количество объектов велико, это принесет большее давление на GC., также косвенно влияет на производительность приложения. Чтобы уменьшить количество временных объектов, выделенных в куче, JVM определяет, что объект не будет доступен извне с помощью escape-анализа. Затем объект декомпозируется и размещается в стеке путем скалярной замены, так что пространство памяти, занимаемое объектом, может быть уничтожено, когда кадр стека выталкивается из стека, что снижает нагрузку на сборку мусора.

Суммировать:

Статьи по анализу ускользания были опубликованы в 1999 году, но не были реализованы до JDK 1.6, а технология еще не очень зрелая.

Фундаментальная причина в том, что нет никакой гарантии, что потребление производительности escape-анализа должно быть выше, чем его потребление. Хотя скалярная замена, выделение стека и устранение блокировки могут быть выполнены после анализа побега. Однако сам анализ побега также требует серии сложных анализов, что на самом деле является относительно трудоемким процессом.

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

Хотя эта технология не очень зрелая, она также является очень важным средством технологии оптимизации компилятора «точно в срок».


5. Область метода

  • Область методов, как и куча Java, является областью памяти, разделяемой всеми потоками.
  • Хотя спецификация виртуальной машины Java описывает область методов как логическую часть кучи, у нее есть псевдоним Non-Heap, который должен быть отделен от области кучи Java.
  • Пул констант времени выполнения является частью области методов. В дополнение к информации описания, такой как версия/поле/метод/интерфейс класса, в файле класса также имеется пул констант (таблица пула констант), который используется для хранения различных литералов и символических ссылок, сгенерированных во время компиляции. , Эта часть содержимого Сохраните класс в пуле констант времени выполнения области методов после загрузки. Также есть возможность заносить новые константы в пул во время работы, эта возможность больше используется разработчиками.String.intern()метод. Ограничено памятью области метода, она будет выброшена, когда постоянный пул больше не может применяться к памяти.OutOfMemoryErroр ненормальный.
  • Размер области метода такой же, как пространство кучи. Вы можете выбрать фиксированный размер или расширяемый размер. Размер области метода определяет, сколько классов можно разместить в системе. Если системных классов слишком много, область метода будет переполнена, и виртуальная машина также выбросит память.Ошибка переполнения
  • Область метода освобождается после закрытия JVM.

5.1 Путаница

Вы также смотрели на различные справочные материалы, на некоторых диаграммах структуры памяти есть области методов, а на некоторых — постоянное поколение, области метаданных, когда вы запутались?

  • область методаТолькоСпецификация JVMодин определен вконцепция, который используется для хранения таких данных, как информация о классе, пул констант, статические переменные и JIT-компилированный код. В нем не указано, как его реализовать. У разных производителей разные реализации. иПостоянное поколение (PermGen)даHotspotКонцепция, специфичная для виртуальных машин, которая снова использовалась в Java8.метапространствоВместо этого и постоянное поколение, и метапространство можно понимать как реализацию области метода.
  • Физика постоянного поколения является частью кучи, а новое поколение, адрес старого поколения является непрерывным (управляется сборщиком мусора), а метапространство существует в локальной памяти (мы часто говорим память вне кучи, не управляемая сборщиком мусора). сборщик), это не ограничено JVM, и OOM сложнее (будут исключения переполнения)
  • В Java7 мы проходим-XX:PermSizeи-xx:MaxPermSizeЧтобы установить параметры постоянной генерации, после Java 8, с отменой постоянной генерации, эти параметры также станут недействительными, а вместо этого пройдут-XX:MetaspaceSizeи-XX:MaxMetaspaceSizeИспользуется для установки параметров метапространства
  • Содержимое хранилища отличается, метаинформация класса хранилища метапространства, статические переменные и пулы констант и т. д. включены в кучу. Данные, эквивалентные постоянной генерации, делятся на кучу и метапространство.
  • Генерируется виртуальной машиной Java, если память в области метода не может быть использована для удовлетворения запроса на выделениеOutOfMemoryError
  • Спецификация JVM говорит, что область методов логически является частью кучи, но в настоящее время она фактически отделена от кучи Java (не куча).

Итак, для области методов изменения после Java8:

  • Удалена постоянная генерация (PermGen) и заменена на Metaspace;
  • Метаданные класса в постоянном поколении переносятся в собственную память (локальную память, а не виртуальную машину);
  • Интернированные строки и статические переменные класса в постоянном поколении были перемещены в кучу Java;
  • Параметры постоянной генерации (PermSize MaxPermSize) -> Параметры метапространства (MetaspaceSize MaxMetaspaceSize)

5.2 Установите размер памяти области метода

JDK8 и более поздние версии:

  • Размер области метаданных может использовать параметры-XX:MetaspaceSizeи-XX:MaxMetaspaceSizeУкажите, замените выше два исходных параметра
  • Значение по умолчанию зависит от платформы. Под Windows,-XX:MetaspaceSize21М,-XX:MaxMetaspacaSizeравно -1, т.е. без ограничений
  • В отличие от постоянного поколения, если вы не укажете размер, по умолчанию виртуальная машина будет использовать всю доступную системную память. Если метаданные переполнятся, виртуальная машина также выдаст исключение.OutOfMemoryError:Metaspace
  • -XX:MetaspaceSize: Установите начальный размер метапространства. Для 64-битной JVM на стороне сервера по умолчанию-XX:MetaspaceSizeЗначение равно 20,75 МБ, что является начальным верхним пределом.По достижении этого предела будет запущен полный сборщик мусора и выгрузит бесполезные классы (то есть загрузчик классов, соответствующий этим классам, больше не работает), а затем верхняя отметка Строка будет сброшена, и значение новой верхней отметки зависит от того, сколько метапространства было освобождено после GC. Если свободного места недостаточно, тоMaxMetaspaceSize, увеличьте это значение соответствующим образом. Если освобождается слишком много места, уменьшите значение соответствующим образом.
  • Если начальная максимальная отметка установлена ​​слишком низко, вышеупомянутая корректировка максимальной отметки будет происходить много раз, и можно заметить, что полный сборщик мусора вызывается несколько раз в журнале сборки мусора. Чтобы избежать частого GC, рекомендуется-XX:MetaspaceSizeУстановите относительно высокое значение.

5.3 Внутренняя структура области метода

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

Информация о типе

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

  • Полное допустимое имя этого типа (fullname=packagename.classname)
  • Полностью допустимое имя прямого суперкласса этого типа (нет суперкласса для интерфейса или java.lang.Object)
  • Модификаторы для этого типа (public, abstract, некоторое подмножество final)
  • упорядоченный список прямых интерфейсов этого типа

Информация о поле

  • JVM должна хранить информацию обо всех полях типа и порядке, в котором поля объявлены в области методов.
  • Информация, связанная с доменом, включает: доменное имя, тип домена, модификатор домена (подмножество общедоступных, частных, защищенных, статических, окончательных, изменчивых, временных)

Информация о методе

JVM должна сохранить

  • имя метода
  • возвращаемый тип метода
  • Количество и типы параметров метода
  • Модификаторы для методов (public, private, protected, static, final, synchronized, native, подмножество абстрактных)
  • Коды символов метода (байт-коды), стек операндов, таблица локальных переменных и размер (кроме абстрактных и собственных методов)
  • Таблица исключений (кроме абстрактных и нативных методов)
    • Начальная позиция и конечная позиция каждого обработчика исключений, адрес смещения обработчика кода в счетчике программ и постоянный индекс пула перехваченного класса исключений.

Взаимодействие между стеком, кучей и областью метода

5.4 Пул констант времени выполнения

Пул констант времени выполнения (пул констант времени выполнения) является частью области методов. Чтобы понять пул констант времени выполнения, давайте сначала поговорим о пуле констант (таблице пула констант) в файле байт-кода (файле классов).

постоянный пул

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

Зачем нужен постоянный бассейн?

Класс и интерфейс в исходном файле Java компилируются для создания файла байт-кода. Байт-код в Java нуждается в поддержке данных.Обычно эти данные настолько велики, что их нельзя напрямую хранить в байт-коде.По-другому их можно хранить в пуле констант.Этот байт-код содержит указатели на пул констант.цитирования. Пул констант времени выполнения используется во время динамического связывания.

Таким образом, мы просматриваем простой класс только с методом Main через jclasslib.#2 в байт-коде указывает на постоянный пул.

Пул констант можно рассматривать как таблицу, и инструкция виртуальной машины находит имя класса, имя метода, тип параметра, литерал и другие типы, которые должны выполняться в соответствии с этой таблицей констант.

постоянный пул времени выполнения

  • После загрузки классов и структур в виртуальную машину создается соответствующий пул констант времени выполнения.
  • Таблица пула констант является частью файла класса и используется для хранения различных литералов и символических ссылок, сгенерированных во время компиляции.Эта часть содержимого будет храниться в пуле констант времени выполнения области методов после загрузки класса.
  • JVM поддерживает постоянный пул для каждого загруженного типа (класса или интерфейса). Доступ к элементам данных в пуле, как и к элементам массива, осуществляется по индексу.
  • Пул констант времени выполнения содержит множество констант, в том числе числовые литералы, которые уже известны компилятору, а также ссылки на методы или поля, которые невозможно получить до разрешения времени выполнения. В настоящее время это уже не символический адрес в пуле констант, и здесь он заменяется реальным адресом.
    • Еще одна важная особенность пула констант времени выполнения по сравнению с пулом констант файла класса:динамизм, язык Java не требует, чтобы константы генерировались только во время компиляции, и новые константы также могут быть помещены в пул во время выполнения.intern()Метод такой
  • Если при создании пула констант времени выполнения класса или интерфейса объем памяти, необходимый для создания пула констант времени выполнения, превышает максимальное значение, указанное в области метода, JVM выдаст исключение OutOfMemoryError.

5.5 Детали развития области методов в JDK6, 7 и 8

Только у HotSpot есть концепция постоянной генерации

jdk1.6 и ранее Существует постоянная генерация, а статические переменные хранятся в постоянной генерации.
jdk1.7 Существует постоянная генерация, но она постепенно «де-постоянная генерация», пул строковых констант и статические переменные удаляются и сохраняются в куче.
jdk1.8 и выше Отмените постоянную генерацию, информация о типах, поля, методы и константы хранятся в метапространстве локальной памяти, но пул строковых констант и статические переменные все еще находятся в куче.

Причина удаления постоянной генерации

openjdk.java.net/jeps/122

  • Установить размер пространства для постоянной генерации сложно.

    В некоторых сценариях, если динамически загружаемых классов слишком много, легко сгенерировать ООМ в районе Перми. Если в реальном веб-проекте много функциональных точек, многие классы должны динамически загружаться во время выполнения процесса, и часто возникает OOM. Самая большая разница между метапространством и постоянным поколением заключается в том, что метапространство находится не в виртуальной машине, а использует локальную память, поэтому по умолчанию размер метапространства ограничен только локальной памятью.

  • Настройка постоянной генерации сложна

5.6 Сбор мусора в методической зоне

Сборка мусора в области метода в основном перерабатывает две части:Устаревшие константы и больше не используемые типы в пуле констант.

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

  • Полные имена классов и интерфейсов
  • Имена полей и дескрипторы
  • имя и дескриптор метода

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

Чтобы определить, принадлежит ли тип к «классу, который больше не используется», должны одновременно выполняться три условия:

  • Все экземпляры этого класса были восстановлены, то есть в куче Java нет экземпляров этого класса и каких-либо производных подклассов.
  • Загрузчик класса, который загрузил класс, был переработан. Этого условия обычно трудно достичь, если только это не хорошо продуманный сценарий альтернативного загрузчика классов, такой как OSGi, перезагрузка JSP и т. д.
  • Объект java.lang.Class, соответствующий этому классу, нигде не упоминается, и методы этого класса нигде не могут быть доступны через отражение

Виртуальной машине Java разрешено перерабатывать бесполезные классы, отвечающие трем вышеприведенным условиям, здесь только «разрешено», а не то же самое, что и объект, который неизбежно будет переработан, если он не используется. Следует ли перерабатывать класс, виртуальная машина HotSpot предоставляет-Xnoclassgcпараметры для управления, вы также можете использовать-verbose:classа также-XX:+TraceClassLoading,-XX:+TraceClassUnLoadingПросмотр информации о загрузке и выгрузке класса.

В сценариях, где используются отражение, динамические прокси, фреймворки ByteCode, такие как CGLib, динамически генерируемые JSP и часто настраиваемые загрузчики классов, такие как OSGi, виртуальная машина должна иметь функцию выгрузки классов, чтобы гарантировать, что постоянное поколение не переполнится.

Ссылка и спасибо

Его можно рассматривать как учебную заметку, общее ободрение, основной источник:

«Понимание виртуальной машины Java, третье издание»

Учебник по JVM от Song Hongkang

docs.Oracle.com/JavaColor/spec…

woo woo .cn блог на .com/ для символа IC F по-французски/ боюсь…

Блог Ву Ву.cn на.com/Холлис Биография…