Я написал очень хардкорный базовый обзор JVM. Его не так просто написать. Друзья, ставьте лайки и пересылайте скорее!
Оригинальная ссылкаГоворят, что на чтение этой JVM уходит час.
Какова основная роль JVM?
JVM — это аббревиатура от Java Virtual Machine (Java Virtual Machine).JVM скрывает информацию, относящуюся к конкретной платформе операционной системы, так что программе Java нужно только генерировать объектный код (байт-код), работающий на виртуальной машине Java, и его можно использовать в разных запусках на платформе.
Не могли бы вы описать область памяти Java?
В процессе выполнения Java-программы JVM делит управляемую ею память на несколько различных областей.Некоторые из этих компонентов являются частными для потоков, а некоторые совместно используются потоками.Область памяти Java также называется областью данных времени выполнения. деление выглядит следующим образом:
-
虚拟机栈
: Стек виртуальной машины Java — это частная область данных потока.Жизненный цикл стека виртуальной машины Java такой же, как и у потока, а стек виртуальной машины также является местом хранения локальных переменных. Во время выполнения метода создается стек виртуальной машины栈帧(stack frame)
. Процесс выполнения каждого метода соответствует процессу укладки и извлечения.
-
本地方法栈
: локальный стек методов также является частной областью данных потока, а область, где хранится локальный стек методов, в основном используется в Java.native
Область, в которой хранятся методы, украшенные ключевыми словами. -
程序计数器
: Счетчик программ также является областью личных данных нити.Эта часть области используется для хранения адреса инструкции нити и используется для оценки функций ветвления, цикла, перехода, исключения, переключения нити и восстановления поток, которые все завершаются счетчиком программ. -
方法区
: область методов — это область памяти, совместно используемая каждым потоком, которая используется для хранения таких данных, как информация о классе, константы, статические переменные и код, скомпилированный компилятором реального времени, загруженным виртуальной машиной. -
堆
: куча — это область данных, разделяемая потоками. Куча — это самая большая область хранения в JVM. Все экземпляры объектов размещаются в куче. После JDK 1.7 пул строковых констант был удален из постоянного поколения и сохранен в куче.Выделение памяти для кучи (по умолчанию):
- Старое поколение: две трети пространства кучи
- Молодое поколение: треть пространства кучи
- Эдемский район: пространство молодого поколения 8/10
- выживший 0 : 1/10 пространства молодого поколения
- Выживший 1: 1/10 пространства молодого поколения
Выполните следующую команду в командной строке, чтобы просмотреть параметры JVM по умолчанию.
java -XX:+PrintFlagsFinal -version
Вывод очень большой, но только две строки могут отражать приведенные выше результаты распределения памяти.
-
运行时常量池
: пул констант времени выполнения также известен какRuntime Constant Pool
, эта область является частью области метода, название у нее очень интересное, обычно называется非堆
. Он не требует, чтобы константы генерировались только во время компиляции, то есть вместо помещения констант в пул констант во время компиляции новые константы также могут быть помещены в пул констант во время выполнения.Типичным примером является внутренний метод String.
Не могли бы вы описать механизм загрузки классов в Java?
Виртуальная машина Java отвечает за загрузку данных, описывающих класс, из файла классов в системную память и обработку данных класса.Проверка, парсинг преобразования и инициализация, и, наконец, сформируйте тип Java, который может быть непосредственно использован виртуальной машиной.类加载机制
.
Класс будет проходить следующие процессы с момента его загрузки в память виртуальной машины и до момента его выгрузки из памяти.
В механизме загрузки классов есть пять шагов, которыезагружать, связывать, инициализировать, использовать и выгружатьэтап, определяется порядок этих пяти этапов.
Стадия связывания будет разделена на три этапа, а именно этапы проверки, подготовки и синтаксического анализа. Порядок этих трех этапов не определен, и эти три этапа обычно интерактивны. Фаза синтаксического анализа обычно начинается после инициализации для поддержки функций связывания во время выполнения языка Java (также известного как动态绑定
).
Об этих процессах поговорим ниже.
нагрузка
Спецификация виртуальной машины Java не имеет принудительных ограничений на то, когда начинать процесс загрузки, поэтому мы можем реализовать это. Загрузка — это первый этап всего процесса загрузки класса, на этом этапе виртуальная машина Java должна выполнить три вещи:
- Получите двоичный поток байтов, определяющий этот класс по его полному имени.
- Преобразует структуру хранения, представленную этим потоком байтов, в структуру данных в области метода области данных времени выполнения.
- Объект класса генерируется в памяти, и этот объект представляет запись доступа к этой структуре данных.
Спецификация виртуальной машины Java не указывает, как получить полное имя, поэтому в отрасли существует множество способов получить полное имя:
- Чтение из ZIP-пакета с последующим переходом в формат JAR, EAR, WAR.
- Наиболее распространенным приложением, полученным из сети, является веб-апплет.
- Динамическая генерация во время выполнения, наиболее часто используемой является технология динамического прокси.
- Созданный другими файлами, такими как сценарий приложения JSP, соответствующий файл класса создается файлом JSP.
- Чтение из базы данных, этот сценарий относительно небольшой.
- Может быть получен из зашифрованных файлов, что является типичной защитой от декомпиляции файлов класса.
Фаза загрузки может быть выполнена либо с помощью встроенного загрузчика классов начальной загрузки виртуальной машины, либо с помощью пользовательского загрузчика классов. Программисты могут управлять доступом к потоку байтов, определяя собственный загрузчик классов.
Загрузка массива не должна создаваться загрузчиком классов, она размещается непосредственно в памяти, но тип элемента массива (тип массива с удаленными всеми измерениями) в конечном итоге загружается загрузчиком классов.
проверять
Следующим этапом после загрузки является проверка, поскольку на предыдущем шаге мы упомянули, что объект класса был сгенерирован в памяти, и этот объект является входом для доступа к его репрезентативной структуре данных, поэтому работа по проверке на этом этапе заключается в том, чтобы убедиться, что поток байтов файла класса находится в Содержимое соответствует требованиям Спецификации виртуальной машины Java, гарантируя, что после запуска информации в виде кода она не будет угрожать безопасности виртуальной машины.
Этап проверки в основном делится на четыре этапа проверки:
- Проверка формата файла.
- Проверка метаданных.
- Проверка байт-кода.
- Проверка символьной ссылки.
проверка формата файла
Этот этап может включать следующие контрольные точки:
- Магическое число начинается с
0xCAFEBABE
начало. - Находятся ли основной и дополнительный номера версий в диапазоне, принятом текущей виртуальной машиной Java.
- Проверьте, есть ли неподдерживаемые типы констант в константах постоянно включенного пула.
- Указывает ли какое-либо из различных значений индекса для констант на несуществующие константы или константы, которые не соответствуют типу.
- Есть ли в константе типа CONSTANT_Utf8_info данные, не соответствующие кодировке UTF8.
- Есть ли какая-либо удаленная или дополнительная информация в различных разделах файла класса и самого файла.
На самом деле точек верификации намного больше, это всего лишь небольшая выдержка из исходного кода HotSpot.
проверка метаданных
Этот этап в основном предназначен для выполнения семантического анализа информации, описанной байт-кодом, чтобы убедиться, что описанная информация соответствует «Спецификации языка Java».
- Убедитесь, что у класса есть родитель (все классы, кроме Object, должны иметь родителя).
- Чтобы убедиться, что родительский класс класса наследуется от класса, которому не разрешено наследовать.
- Если этот класс не является абстрактным классом, то реализует ли этот класс все методы, требуемые в родительском классе или интерфейсе.
- Не перезаписано ли конечное поле, есть ли несовместимая перегрузка и т. д.
Имейте в виду, что этот этап является просто проверкой Спецификации языка Java.
Проверка байт-кода
Фаза проверки байт-кода является наиболее сложной фазой, и эта фаза в основном определяет, является ли семантика программы законной и логичной. Этот этап в основном предназначен для проверки и анализа тела метода класса (атрибут Code в файле класса). Эта часть проверки включает
- Убедитесь, что тип данных стека операндов соответствует типу данных фактического выполнения.
- Гарантируется, что любые инструкции перехода не будут переходить к инструкциям байт-кода за пределами тела метода.
- Убедитесь, что преобразование типа в теле метода допустимо.Например, вы можете назначить объект подкласса типу данных суперкласса, но вы не можете назначить тип данных суперкласса подклассу и другим небезопасным преобразованиям типов.
- Другие проверки.
Если проверка байт-кода не пройдена, значит, есть проблема с проверкой. Однако нет необходимости проходить проверку байт-кода, чтобы убедиться, что программа безопасна.
Проверка символьной ссылки
Заключительный этап проверки происходит, когда виртуальная машина преобразует символическую ссылку в прямую ссылку Это преобразование происходит на третьем этапе ссылки, этапе синтаксического анализа. Верификацию символьных ссылок можно рассматривать как проверку соответствия различных типов информации, кроме самого класса.
- Может ли полное имя строки в ссылке на символ найти соответствующий класс.
- Указывает, существует ли в классе дескриптор поля, совместимый с методом, а также метод и поле, описываемые простым именем.
- Может ли доступность методов класса и полевых методов, указанных символом, доступ к текущему классу.
- Другие проверки.
Этот этап в основном предназначен для обеспечения нормального выполнения синтаксического анализа.Если проверка ссылки на символ не может быть пройдена,IllegalAccessError
,NoSuchFieldError
,NoSuchMethodError
и т.п. ошибка.
Этап проверки очень важен для виртуальной машины, если она сможет пройти проверку, это означает, что ваша программа не окажет никакого влияния при работе.
Подготовить
Фаза подготовки — это фаза выделения памяти для переменных в классе и установки их начальных значений.Память, используемая этими переменными, должна быть выделена в области методов.До JDK 7 HotSpot использовал постоянную генерацию для реализации области методов, т.е. в соответствии с этой логической концепцией. После JDK 8 переменные хранятся в куче Java вместе с объектом класса.
Начальные значения примитивных типов и ссылочных типов в следующих обычных случаях
В дополнение к «обычному случаю», есть некоторые «исключительные случаи», если атрибут поля класса существует.ConstantValue
свойство, то значение переменной будет инициализировано до начального значения, указанного свойством ConstantValue на начальном этапе, например
public static final int value = "666";
Во время компиляции значение value устанавливается равным 666.
Разобрать
Фаза синтаксического анализа — это процесс, посредством которого виртуальная машина Java заменяет символические ссылки в пуле констант прямыми ссылками.
-
符号引用
: Символическая ссылка Набор символов, описывающих указанную цель. Символические ссылки могут быть литералами в любой форме, при условии, что они могут быть использованы для определения цели без двусмысленности.Символические ссылки не имеют ничего общего с макетом виртуальной машины. -
直接引用
: Прямая ссылка может быть указателем непосредственно на цель, относительно дешевой величиной или дескриптором, который может косвенно указывать на цель. Прямые ссылки связаны с компоновкой виртуальной машины, и прямые ссылки, переведенные разными виртуальными машинами для одной и той же символической ссылки, обычно отличаются. Если есть прямая ссылка, цель прямой ссылки должна быть загружена в память.
Вы все еще можете быть немного смущены этим, поэтому позвольте мне сказать по-другому:
При компиляции каждый класс Java будет скомпилирован в файл класса, но виртуальная машина не знает адрес ссылочного класса при компиляции, поэтому вместо этого используется символическая ссылка, и эта фаза синтаксического анализа предназначена для преобразования этого этапа. ссылки преобразуются в реальные адреса.
«Спецификация виртуальной машины Java» не указывает время, когда происходит фаза синтаксического анализа, а требует только anewarray, checkcast, getfield, getstatic, instanceof, invokedynamic, invokeinterface, invokespecial, invokestatic, invokevirtual, ldc, ldc_w, ldc2_w, multianewarray, new , putfield И putstatic перед этими 17 инструкциями байт-кода для манипулирования символьной ссылкой анализируется используемая символьная ссылка.
Анализ также разделен на четыре этапа.
- Класс или разрешение интерфейса
- разбор поля
- Разбор метода
- Анализ метода интерфейса
инициализация
Инициализация — последний шаг в процессе загрузки класса, на предыдущих этапах доминировала виртуальная машина Java, но на этом этапе инициатива передается приложению.
Для этапа инициализации «Спецификация виртуальной машины Java» строго оговаривает, что только следующие шесть случаев вызовут инициализацию класса.
- При встрече с четырьмя инструкциями байт-кода new, getstatic, putstatic или invokestatic, если инициализация не была выполнена, инициализация запускается первой. Из названий этих четырех байт-кодов можно судить, что эти четыре байт-кода на самом деле являются двумя сценариями: при вызове нового ключевого слова для инициализации, при чтении или установке статического поля и при вызове статического метода.
- При инициализации класса, если родительский класс не был инициализирован, то сначала необходимо инициализировать родительский класс.
- При использовании методов пакета java.lang.reflect для выполнения вызовов отражения.
- При запуске виртуальной машины, когда пользователю нужно указать основной класс для выполнения, грубо говоря, виртуальная машина сначала инициализирует основной класс метода.
- При использовании недавно добавленной поддержки динамического языка в JDK 7, если конечным результатом синтаксического анализа экземпляра jafva.lang.invoke.MethodHandle является REF_getstatic, REF_putstatic, REF_invokeStatic и REF_newInvokeSpecial, четыре типа дескрипторов методов и класс, соответствующий этому дескриптору метода не обрабатывается. Если он чрезмерно инициализирован, его необходимо сначала инициализировать.
- Когда интерфейс определяет новый метод по умолчанию, добавленный JDK 8 (метод интерфейса, измененный ключевым словом по умолчанию), если класс реализации этого интерфейса инициализирован, интерфейс должен быть инициализирован перед ним.
На самом деле, о вышеперечисленном нужно знать только первым четырем людям, а последние двое относительно непопулярны.
Если вы хотите ответить на вопрос о загрузке класса, вы можете поговорить об этом здесь, но для полноты картины мы просто говорим о последних двух процессах.
использовать
На данном этапе сказать нечего, то есть код после инициализации динамически вызывается и выполняется JVM.
удалить
Когда объект Class, представляющий класс, больше не упоминается, жизненный цикл объекта Class заканчивается, и соответствующие данные в области методов также выгружаются.
⚠️ Но нужно обратить внимание: классы, загруженные загрузчиком классов, который идет в комплекте с JVM, не будут выгружены, а классы, загруженные пользовательским загрузчиком классов, могут быть выгружены.
Как создаются объекты в JVM?
Если мы хотим ответить, как создается объект, ответ, который мы обычно думаем,new
Просто выходите, этот ответ не только ограничен программированием, но и интегрирован во все аспекты нашей жизни.
Но когда вас берут на собеседование, очевидно, неприемлемо просто отвечать «выходит новое», потому что на собеседовании вас обычно просят объяснить, что происходит внутри программы, когда она выполняет новую инструкцию.
Поэтому вам нужно объяснить это с точки зрения JVM.
Когда виртуальная машина встречает новую инструкцию (на самом деле это байт-код), она сначала проверяет, могут ли параметры этой инструкции найти символьную ссылку класса в пуле констант, и проверяет, может ли класс, представленный символической ссылкой, был загружен, проанализирован и инициализирован.
Поскольку на данный момент вы, вероятно, не знаете, что такое конкретный класс, здесь используется символическая ссылка.
Если обнаруживается, что этот класс не прошел описанный выше процесс загрузки класса, то выполняется соответствующий процесс загрузки класса.
После завершения проверки класса следующая виртуальная машина будет новым объектом.Выделить память, требуемый размер объекта определяется после загрузки класса (о чем я расскажу в вопросе интервью ниже).
Выделение памяти эквивалентно отделению фиксированного блока памяти от кучи. После разделения виртуальная машина инициализирует выделенное пространство памяти нулевым значением, если оно используется.TLAB
(локальный буфер выделения потока), эта работа по инициализации может быть выполнена заранее, когда TLAB выделен. Этот шаг гарантирует, что поля экземпляра объекта могут использоваться непосредственно в коде Java без назначения.
Затем виртуальная машина Java также выполнит необходимые настройки объекта, такие как определение класса, экземпляром которого является объект, хэш-код объекта и информация о возрасте генерации объекта GC. Эта информация хранится в заголовке объекта.
Если вышеуказанная работа проделана, с точки зрения виртуальной машины создается новый объект, но для программиста создание объекта только началось, потому что конструктор, то есть в файле класса<init>()
Метод еще не выполнен, и все поля имеют нулевое значение по умолчанию. будет выполняться после новой инструкции<init>()
метод, а затем инициализируйте объект в соответствии с пожеланиями программиста, чтобы объект мог быть полностью сконструирован.
Какие существуют методы распределения памяти?
После загрузки класса виртуальная машина должна выделить память для нового объекта Выделение памяти для объекта эквивалентно выделению определенной области из кучи, что сопряжено с проблемой.Является ли разделяемая область кучи регулярной.
Предполагая, что память в куче Java обычная, вся используемая память откладывается в сторону, неиспользуемая память откладывается в сторону, а в середину помещается указатель, который является индикатором разграничения. Тогда выделение памяти для нового объекта эквивалентно перемещению указателя в свободное место на расстояние, равное размеру объекта.Этот метод выделения памяти называется指针碰撞(Bump The Pointer)
.
Если память в куче Java не регулярна, использованная память и неиспользованная память чередуются друг с другом, в этом случае нет возможности использовать столкновение указателей, вот еще один способ записи использование памяти. :空闲列表(Free List)
, список свободных мест поддерживает список, который записывает, какие блоки памяти доступны, находит в списке достаточно большое пространство для выделения экземпляру объекта при выделении и обновляет записи в списке.
Следовательно, какой из двух вышеперечисленных методов выделения выбрать, зависит от того, является ли куча Java обычной или нет. В реализации некоторых сборщиков мусора сборщики с процессом уплотнения типа Serial и ParNew используют коллизию указателей, при использовании сборщика на базе CMS на основе алгоритма очистки используется свободный список, а о конкретной сборке мусора поговорим об этом позже.
Не могли бы вы рассказать мне о структуре памяти объекта?
существуетhotspot
В виртуальной машине расположение объектов в памяти разделено на три области:
对象头(Header)
实例数据(Instance Data)
对齐填充(Padding)
Распределение памяти этих трех областей показано на следующем рисунке.
Давайте подробнее рассмотрим, что находится в вышеуказанном объекте.
заголовок объекта
Заголовок объекта Header в основном содержит MarkWord и указатель объекта Klass Pointer, если это массив, он также содержит длину массива.
В 32-битной виртуальной машине MarkWord, Klass Pointer и длина массива занимают по 32 бита, что составляет 4 байта.
Если это 64-битная виртуальная машина, MarkWord, Klass Pointer и длина массива занимают 64 бита соответственно, что составляет 8 байт.
Размер байтов, занимаемых Mark Word 32-битной виртуальной машины и 64-битной виртуальной машины, различен, Mark Word и Klass Pointer 32-битной виртуальной машины занимают 32 бита байт соответственно, а Mark Word и Klass 64-битной виртуальной машины виртуальная машина занимает 32 бита байтов соответственно Указатель занимает 64 бита байтов Давайте возьмем 32-битную виртуальную машину в качестве примера, чтобы увидеть, как распределяются байты ее Mark Word.
переведено на китайский
- лицо без гражданства
无锁
Когда заголовок объекта используется для хранения хэш-кода объекта, 4 бита используются для хранения возраста поколения, 1 бит используется для хранения флага блокировки, а 2 бита используются для хранения флага блокировки как 01. -
偏向锁
25 бит используются для хранения идентификатора потока, 2 бита используются для хранения эпохи, 4 бита используются для хранения возраста генерации и 1 бит используется для хранения идентификатора блокировки смещения, 0 означает отсутствие блокировки, 1 означает блокировку смещения , идентификационный бит блокировки по-прежнему равен 01. -
轻量级锁
30-битное пространство напрямую открывается для хранения указателя на запись блокировки в стеке, а 2 бита используются для хранения бита флага блокировки, а бит флага равен 00. -
重量级锁
Как и в легковесной блокировке, 30 бит пространства используются для хранения указателя на тяжеловесную блокировку, а 2 бита используются для хранения идентификационного бита блокировки, который равен 11. -
GC标记
30-битное пространство памяти открыто, но не занято, а 2-битное пространство для хранения флага блокировки равно 11.
Биты флага блокировки блокировки без блокировки и блокировки со смещением равны 01, но первый бит 1 определяет, является ли это состоянием без блокировки или состоянием блокировки со смещением.
Что касается того, почему память распределяется таким образом, мы можем получить ее изOpenJDK
серединаmarkOop.hppПеречисление в классе дает представление
объяснять
- age_bits — это идентификатор, который мы называем рециркуляцией поколений, занимающий 4 байта.
- lock_bits — бит флага блокировки, занимающий 2 байта
- biad_lock_bits — флаг смещения блокировки, занимающий 1 байт.
- max_hash_bits — количество байтов, занимаемых хэш-кодом для безблокировочного вычисления, если это 32-битная виртуальная машина, то 32 — 4 — 2 — 1 = 25 байт, если это 64-битная виртуальная машина, 64 - 4 - 2 - 1 = 57 байт, но 25 байт будут неиспользованными, поэтому 64-битный хэш-код занимает 31 байт.
- hash_bits для 64-битной виртуальной машины, если максимальное количество байт больше 31, берем 31, иначе берем реальное количество байт
- cms_bits Я думаю, он должен занимать 0 байт, если это 64-битная виртуальная машина, или 1 байт, если она 64-битная.
- epoch_bits — размер байтов, занятых эпохой, 2 байта.
В приведенной выше таблице распределения заголовков объектов виртуальной машины мы видим, что существует несколько состояний блокировки: нет блокировки (без сохранения состояния), предвзятые блокировки, облегченные блокировки, усиленные блокировки, где упрощенные блокировки и предвзятые блокировки В JDK1.6 синхронизированная блокировка недавно добавлен после оптимизации, и его цель — значительно оптимизировать производительность блокировки, поэтому в JDK 1.6 накладные расходы на использование synchronized не так велики. На самом деле, с точки зрения того, заблокирована блокировка или нет, есть только блокировки и тяжеловесные блокировки, появление смещенных замков и облегченных замков только увеличивает производительность захвата блокировки, а новые блокировки не появляются.
Поэтому наше внимание сосредоточено на изучении синхронизированных тяжеловесных блокировок.Когда монитор удерживается потоком, он будет находиться в заблокированном состоянии. В виртуальной машине HotSpot базовый код монитораObjectMonitor
Основная структура данных следующая (находится в файле ObjectMonitor.hpp исходного кода виртуальной машины HotSpot, реализована на C++)
В этом C++ есть несколько атрибутов, на которые следует обратить внимание: _WaitSet , _EntryList и _Owner, каждый поток, ожидающий получения блокировки, будет инкапсулирован какObjectWaiter
объект.
_Owner — это поток, указывающий на объект ObjectMonitor, а _WaitSet и _EntryList используются для сохранения списка каждого потока.
Так в чем разница между этими двумя списками? Позвольте мне поговорить с вами о процессе получения блокировки, и вы поймете этот вопрос.
два списка замков
Когда несколько потоков обращаются к определенному коду синхронизации одновременно, они сначала попадают в коллекцию _EntryList.Когда поток получает монитор объекта, он входит в область _Owner и указывает _Owner объекта ObjectMonitor на текущий поток. , и сделайте _count + 1, если вызывается операция по снятию блокировки (например, ожидание), текущий удерживаемый монитор будет освобожден, owner = null, _count - 1, а поток войдет в список _WaitSet и будет ждать до быть разбужен. Если текущий поток завершит выполнение, блокировка монитора также будет снята, но в это время он не войдет в список _WaitSet, а напрямую сбросит значение _count.
Klass Pointer представляет собой указатель типа, то есть указатель на метаданные класса объекта, и виртуальная машина использует этот указатель, чтобы определить, экземпляром какого класса является объект.
Возможно, вы не очень хорошо понимаете концепцию указателя, вы можете просто понять, что указатель — это адрес, указывающий на некоторые данные.
Данные экземпляра Данные экземпляра
Часть данных экземпляра — это эффективная информация, которую фактически хранит объект, а также размер в байтах каждого поля, определенного в коде.Например, байт занимает 1 байт, а int — 4 байта.
Выровнять отступы
Выравнивание не обязательно должно существовать, оно просто выступает в качестве заполнителя (%d, %c и т. д.). Это требование JVM, потому что JVM HotSpot требует, чтобы начальный адрес объекта был целым числом, кратным 8 байтам, что означает, что размер объекта в байтах является целым числом, кратным 8, и если он не достаточно, вам нужно использовать Padding, чтобы завершить его.
Какие существуют методы доступа к объектам и их позиционирования?
Конечно, целью создания объекта является его использование, но как после создания объекта JVM получает доступ к этому объекту? Обычно есть два пути:доступ по ручкеа такжеДоступ через прямой указатель.
-
Если используется метод доступа к дескриптору, часть памяти может быть разделена на пул дескрипторов в куче Java, адрес дескриптора объекта хранится в ссылке, а дескриптор содержит конкретную информацию об адресе данных и типа экземпляра. данные объекта. Как показано ниже.
-
Если вы используете прямой доступ по указателю, структура памяти объектов в куче Java будет другой. Ссылка на область стека указывает адрес данных экземпляра в куче. Если вы обращаетесь только к самому объекту, прямого доступа больше не будет. overhead. , а указатель данных типа объекта существует в области метода. Если он находится, ему нужен еще один прямой заголовок местоположения. Как показано ниже
Эти два метода доступа к объектам имеют свои преимущества.Самое большое преимущество использования дескрипторов заключается в том, что адрес дескриптора хранится в ссылке.При перемещении объекта вам нужно только изменить адрес дескриптора без изменения самого объекта .
Использование прямых указателей для доступа быстрее, это экономит время на исправление указателя, так как доступ к объектам очень частый в Java, потому что этот вид накладных расходов также стоит оптимизировать.
Выше говорилось о двух видах данных объекта, один из которых является данными экземпляра объекта, о чем нечего сказать, это данные поля экземпляра объекта, другой - данные типа объекта, эти данные относятся к типу, родительскому классу, реализации интерфейса и методов объекта и т. д.
Как я могу определить, умер ли субъект?
Мы все знаем, что в основном все объекты распределяются в куче.Когда мы больше не используем объекты, сборщик мусора переработает бесполезные объекты♻️, так как же JVM определяет, какие объекты уже являются «бесполезными объектами»?
Здесь есть два способа судить, сначала поговорим о первом:подсчет ссылок.
Критерий подсчета ссылок следующий: добавьте счетчик ссылок к объекту, и всякий раз, когда есть место для ссылки на него, значение счетчика будет увеличиваться на единицу; когда ссылка недействительна, значение счетчика будет уменьшить на единицу, если любой объект, счетчик времени которого равен нулю, является объектом, который больше не будет использоваться. Хотя этот метод оценки очень прост и груб, он часто очень полезен.Однако в области Java основная реализация виртуальной машины Hotspot не использует этот метод, потому что метод подсчета ссылок не может решить проблему циклических ссылок между объектами.
Проблема циклической ссылки заключается просто в том, что два объекта зависят друг от друга, а других ссылок нет, поэтому виртуальная машина не может определить, является ли ссылка нулевой, и выполнить операции по сборке мусора.
Другой способ судить о бесполезности объекта состоит в том, чтобыАлгоритмы анализа достижимости.
Текущая основная JVM использует для вынесения сужденийалгоритм анализа достижимости.Основная идея этого алгоритма заключается в использовании рядаGC Roots
Корневой объект используется в качестве начального набора узлов, начиная с этих узлов, поиск вниз в соответствии с эталонным отношением, путь, пройденный процессом поиска, называется引用链
(Цепочка ссылок), если нет цепочки ссылок между объектом и GC Roots или когда объект недоступен из GC Roots, это доказывает, что этот объект является бесполезным объектом и нуждается в сборке мусора.
Эта ссылка выглядит следующим образом
Как показано на рисунке выше, обход начинается с корневого узла перечисления GC Roots.Объекты 1, 2, 3 и 4 являются объектами со ссылочными отношениями, и хотя объекты 5, 6 и 7 связаны, они находятся между GC Roots. .Время не большое, поэтому считается утилизируемым объектом.
В технологической системе Java объекты, которые могут быть извлечены как корни GC, в основном:
-
Объекты, на которые есть ссылки в стеке виртуальной машины (таблица локальных переменных во фрейме стека).
-
Объекты, на которые ссылаются статические свойства класса в области методов, такие как статические переменные ссылочного типа классов Java.
-
Объекты, на которые ссылаются константы в области методов, такие как ссылки в пуле строковых констант.
-
Объекты, на которые ссылается JNI в собственном стеке методов.
-
Ссылки внутри JVM, такие как объекты класса, соответствующие базовым типам данных, некоторые объекты исключений, такие как NullPointerException, OutOfMemoryError и т. д., и загрузчик системных классов.
-
Все объекты, удерживаемые синхронизированы.
-
Есть также некоторые внутренние компоненты JVM, такие как JMXBean, обратные вызовы, зарегистрированные в JVMTI, собственный кеш кода и т. д.
-
В зависимости от сборщика мусора, выбранного пользователем, и текущей освобожденной области памяти некоторые объекты могут быть временно добавлены для формирования коллекции GC Roots.
Хотя выше мы упомянули два метода оценки повторного использования объектов, ни метод подсчета ссылок, ни оценка корней сборщика мусора неотделимы друг от друга.引用
этот уровень отношений.
Это включает отношения цитирования сильных цитирований, мягких цитирований, слабых цитирований и виртуальных цитирований.Вы можете прочитать эту статью автора.
Будьте осторожны, чтобы не быть сборщиком мусора.
Как определить, что класс больше не используется?
Решение о том, что тип принадлежит к «больше не используемому классу», должно соответствовать следующим трем условиям.
- Все экземпляры этого класса были утилизированы, то есть класс и любые экземпляры этого класса не существуют в куче Java.
- Загрузчик классов, который загрузил этот класс, был переработан, но загрузчик классов, как правило, трудно переработать, если только загрузчик классов не предназначен для этой цели, такой как перезагрузка OSGI, JSP и т. Д., Обычно этого трудно добиться.
- Объект Class, соответствующий этому классу, нигде не упоминается, и к свойствам и методам этого класса нельзя получить доступ через отражение в любое время.
Виртуальная машина позволяет выполнять операции по освобождению бесполезных классов, соответствующих трем указанным выше условиям.
Каковы теории коллекции поколений JVM?
Большинство коммерческих виртуальных машин следуютКоллекция поколенийВ теории поколенческих коллекций есть две основные гипотезы.
первыйГипотеза сильного поколения, гипотеза сильного поколения относится к тому факту, что JVM считает, что жизненный цикл большинства объектов эфемерен;
Второйгипотеза слабого поколения, гипотеза слабой генерации означает, что пока объект, выживший в процессе сборки мусора, труднее перерабатывать (похоже, что у объекта тоже будет разум).
Основываясь на этих двух гипотезах, JVM будет堆
Область делится на разные области, а затем объекты, которые необходимо переработать, распределяются по разным областям для хранения в соответствии с тем, сколько раз они пережили сборку мусора.
Согласно этим двум теориям коллекции поколений, JVM делит область кучи на молодое поколение.а такжеСтарое поколение** эти две области. В новом поколении во время каждой сборки мусора обнаруживается большое количество мертвых объектов, а оставшиеся не умершие объекты будут напрямую переведены в старое поколение.
Вышеуказанные две гипотезы не рассматривают референтное отношение объектов, но дело в том, что референтное отношение между объектами будет.На основании этого родилась третья гипотеза, а именноЭталонная гипотеза о межпоколении, ссылки из разных поколений составляют лишь меньшинство по сравнению с ссылками из одного поколения.
Обычно два объекта, которые ссылаются друг на друга, должны жить и умирать вместе, но есть особые случаи.Если объект нового поколения ссылается на объект старого поколения из поколения в поколение, объект нового поколения не будет восстановлен во время сборки мусора.Объект нового поколения не будет перерабатывать объект старого поколения, а затем объект нового поколения переживет сборку мусора и войдет в старое поколение.В это время ссылка на перекрестное поколение будет устранена.
Согласно гипотезе межпоколенческих ссылок, нам не нужно напрямую сканировать все старое поколение, потому что в старом поколении есть несколько межпоколенческих ссылок, а также нам не нужно вести список в старом поколении, чтобы записать, какие пересечения существуют ссылки на поколения.Запомненный набор, старость делится на несколько небольших блоков этим набором памяти, который определяет, какой блок старости будет иметь межпоколенческие ссылки.
Иллюстрация набора памяти выглядит следующим образом
Из рисунка видно, что каждый элемент в наборе памяти соответствует тому, есть ли в непрерывной области памяти межпоколенческий эталонный объект. Если да, то область будет помечена как «грязная», иначе она будет « чистый""(чистый). Таким образом, во время сборки мусора необходимо только просканировать набор памяти, чтобы просто определить расположение ссылок между поколениями, что является типичной идеей пространства-в-времени.
Расскажите об алгоритме сборки мусора в JVM?
Прежде чем говорить о конкретном алгоритме сборки мусора, необходимо уточнить, какие объекты должен собирать сборщик мусора? Иными словами, какие объекты нужно в первую очередь считать «мусором»?
Я выше стандарта сужденияКак узнать, мертв ли объектКак описано в вопросе, есть два способа, один из которых - метод подсчета ссылок, этот критерий заключается в добавлении счетчика ссылок к объекту, ссылка на этот объект сделает значение счетчика + 1, после того, как ссылка недействительна, значение счетчика будет -1. Но этот метод не может решить проблему циклических ссылок между объектами.
Другой метод - GC Roots. GC Roots использует корневой узел в качестве ядра и постепенно ищет ссылку каждого объекта. Путь, пройденный поиском, называется цепочкой ссылок. Если после цепочки поиска нет ссылки на этот объект , то этот объект бесполезен и может быть переработан. GC Roots может решить проблему циклической ссылки, так что это то, что принимает общая JVM.
Описание кода разрешения циклических ссылок:
public class test{
public static void main(String[]args){
A a = new A();
B b = new B();
a=null;
b=null;
}
}
class A {
public B b;
}
class B {
public A a;
}
На основе идеи GC Roots разработано множество алгоритмов сборки мусора, об этих алгоритмах поговорим ниже.
алгоритм маркировки-развертки
**Mark-Sweep** Можно сказать, что этот алгоритм является самым ранним и основным алгоритмом.Mark-Sweep, как следует из названия, делится на два этапа, а именно этапы маркировки и очистки: сначала помечаются все объекты, которые необходимо для переработки, а затем после завершения маркировки все отмеченные объекты равномерно перерабатываются. Конечно, вы также можете отмечать уцелевшие объекты и перерабатывать неотмеченные объекты. Этот процесс маркировки является процессом определения мусора.
Большинство последующих алгоритмов сборки мусора основаны на идее алгоритма пометки, но последующие алгоритмы компенсируют недостатки алгоритма пометки и очистки, так в чем же заключаются недостатки? Есть в основном два
- Эффективность исполнения нестабильна, потому что, если в куче находится большое количество бесполезных объектов, и большинство из них необходимо переработать, в это время необходимо выполнить большое количество маркировки и очистки, что приводит к эффективности выполнения двух процессов маркировки. а просвет уменьшается с увеличением количества объектов.
- фрагментация памяти, алгоритм маркировки-очистки создаст большое количество несмежных фрагментов памяти в области кучи. Слишком сильная фрагментация может привести к нехватке места при выделении больших объектов и необходимости выполнять операцию сборки мусора.
Схематическая диаграмма алгоритма маркировки выглядит следующим образом.
алгоритм пометки-копии
Поскольку алгоритм пометки-чистки очень подвержен фрагментации памяти, исследователи предложили алгоритм пометки-копии, который также можно назвать алгоритмом копирования.Репликация половинной области, он разделит размер памяти на два равных блока, будет использовать только один из них за раз, использовать другой блок, когда блок будет израсходован, а затем очистить используемый блок. Хотя проблема частичной фрагментации памяти решена, алгоритм репликации также приносит новые проблемы, а именнонакладные расходы на репликацию, но эти накладные расходы можно уменьшить.Если большинство объектов в памяти являются бесполезными объектами, то несколько уцелевших объектов можно скопировать, а затем бесполезные объекты можно переработать.
Однако недостатки алгоритма копирования также очевидны, то есть объем памяти уменьшается вдвое по сравнению с оригиналом, а трата места слишком очевидна. Схематическая диаграмма алгоритма маркировки-копии выглядит следующим образом.
Большинство виртуальных машин Java теперь используют этот алгоритм для повторного использования нового поколения, потому что исследования показывают, что 98% объектов нового поколения не могут пережить первый раунд сбора, поэтому нет необходимости делить новое поколение по соотношению 1:1 объем памяти.
Основываясь на этом, исследователи предложили переработку в стиле Аппеля.Специфический метод переработки в стиле Аппеля состоит в том, чтобы разделить новое поколение на более крупныеEdenкосмос и дваSurvivorпространство, используйте только Eden и часть пространства Survivor каждый раз, когда выделяется память.Когда происходит сборка мусора, скопируйте объекты, все еще живые в Eden и Survivor, в другую часть пространства Survivor за один раз, а затем непосредственно очистите Eden и использованные объекты , Пространство выжившего.
В основных виртуальных машинах HotSpot соотношение размеров Eden и Survivor по умолчанию составляет 8:1, то есть доступное пространство памяти в каждом новом поколении составляет 90% от емкости всего нового поколения, и существует только одно пространство Survivor. поэтому 10% будет потрачено впустую. Это 8:1 является лишь теоретическим значением, то есть нет гарантии, что каждый раз выживет не более 10% объектов, поэтому, если Survivor не может разместить выживающие объекты после сборки мусора, другое пространство памяти необходимо провестипомощь, этот метод называетсяГарантия памяти (продвижение ручки), как правило, в качестве гарантии используется старость.
алгоритм маркировки-сопоставления
Хотя алгоритм пометки-копии решает проблему фрагментации памяти, он не решает проблему больших накладных расходов при копировании объектов. Для устранения дефектов алгоритма копирования и полного использования пространства памяти предлагается алгоритм сортировки по отметке. Фаза маркировки алгоритма аналогична маркировке-очистке, но вместо того, чтобы очищать повторно используемые объекты сразу после маркировки, он перемещает все живые объекты в один конец, а затем очищает память за конечной границей. Конкретный процесс показан на следующем рисунке:
Что такое набор памяти и что такое карточный стол? Какова связь между наборами памяти и карточными таблицами?
Для решения проблемы межпоколенческой ссылки предлагается понятие набора памяти.Набор памяти представляет собой структуру данных, используемую в новом поколении.Он эквивалентен набору указателей,указывающих на какие объекты в старом поколении имеют межпоколенческие ссылки.
Наборы памяти реализованы с разной степенью детализации
- точность длины слова: Каждая запись имеет точность до одного слова. Длина машинного слова — это биты адресации процессора, например, обычных 32-битных или 64-битных процессоров. Эта точность определяет длину указателя для доступа машины к физической памяти. адрес Указатель поколения.
- точность объекта: каждая запись соответствует объекту, содержащему указатель между поколениями.
- Точность карты: Каждая запись соответствует области памяти, содержащей указатели перекрестного поколения.
Среди них точность карт реализуется с использованием таблицы карт в качестве набора памяти.Что касается отношений между набором памяти и таблицей карт, вы можете представить отношения между HashMap и Map.
Что такое карточная страница?
Таблица карт на самом деле представляет собой массив байтов.
CARD_TABLE[this address >> 9] = 0;
Каждый элемент массива байтов CARD_TABLE соответствует блоку памяти определенного размера в области памяти.страница карты, Вообще говоря, страница карты — это количество байтов в N степени 2. Из приведенного выше кода мы можем узнать, что страница карты обычно равна 2 в 9-й степени, что также является страницей карты, используемой в HotSpot, что есть, 512 байт.
Память страницы карточки обычно содержит более одного объекта.Пока в поле объекта на странице карточки есть перекрестный указатель генерации, значение элемента массива, соответствующего таблице карточки, устанавливается равным 1, что называется изменением этого элемента.脏
или 0, если не отмечено. Во время сборки мусора, пока вы отфильтровываете грязные элементы в таблице карт, вы можете легко выяснить, какие блоки памяти страниц карты содержат указатели перекрестного поколения, а затем добавить их в GC Roots для сканирования.
Поэтому карточные страницы и карточные таблицы в основном используются для решения проблемы межпоколенческих ссылок.
Что такое барьер записи? Проблемы с барьерами записи?
Если в других областях генерации есть объекты, которые ссылаются на объекты в этой области, соответствующие элементы карточной таблицы станут грязными.Эта ссылка относится к назначению объекта, что означает, что элементы карточной таблицы станут грязными при назначении объекта. обновлять таблицу карт ТО при назначении объекта?
Виртуальная машина HotSpot используетБарьер записиДля сохранения состояния карточного стола этот барьер записи совершенно не похож на наш барьер памяти, надеюсь, читатели его не перепутают.
Этот барьер записи на самом деле является аспектом Aop, который генерируетУведомление о звонке (около), циклическое уведомление должно генерировать уведомление до и после вырезания, потому что это барьер записи, поэтому часть барьера записи перед назначением называется барьером до записи, а та, что после назначения, называется публикацией - записывающий барьер.
Барьеры записи создают две проблемы
Накладные расходы на производительность, вызванные безусловными барьерами записи
Каждый раз, когда обновляется ссылка, независимо от того, обновляется ли ссылка старого поколения на объект молодого поколения, будет выполняться операция барьера записи. Очевидно, что это добавляет некоторые дополнительные накладные расходы. Однако по сравнению со сканированием всего старого поколения эти накладные расходы намного ниже.
Однако в среде с высокой степенью параллелизма барьеры записи приводят к ложным проблемам совместного использования.
Накладные расходы на производительность, вызванные ложным совместным использованием при высокой степени параллелизма
В случае высокой степени параллелизма частые барьеры записи склонны к ложному совместному использованию, что приводит к снижению производительности.
Предполагая, что размер строки кэша ЦП составляет 64 байта, поскольку запись таблицы карточек занимает 1 байт, это означает, что 64 записи таблицы карточек будут совместно использовать одну и ту же строку кэша.
Каждая страница карты HotSpot занимает 512 байт, поэтому одна строка кэша будет соответствовать 64 страницам карты, всего 64 * 512 = 32 КБ.
Если операции обновления ссылок на объекты разными потоками оказываются в одной и той же области размером 32 КБ, это приведет к одновременному обновлению одной и той же строки кэша таблицы карт, что приведет к обратной записи, аннулированию или синхронизации данных. строки кэша, что косвенно влияет на производительность программы.
Простое решение состоит в том, чтобы не использовать безусловный барьер записи, а сначала проверить флаг таблицы карточек и пометить запись таблицы карточек как грязную, только если она не была помечена.
Это обходной путь, представленный в JDK 7, в котором представлен новый параметр JVM.-XX:+UseCondCardMark, перед выполнением барьера записи сделайте простое суждение. Если страница карты уже была помечена, она не будет помечена повторно.
Простое понимание заключается в следующем:
if (CARD_TABLE [this address >> 9] != 0)
CARD_TABLE [this address >> 9] = 0;
По сравнению с исходной реализацией просто добавлена операция суждения.
Хотя открытый-XX:+UseCondCardMarkПосле этого возникает больше накладных расходов, но это позволяет избежать проблемы одновременной записи карт, которая может возникнуть в ситуациях с высокой степенью параллелизма. Сокращение числа одновременных операций записи позволяет избежать ложного совместного использования.
Что такое трехцветная маркировка? Какие проблемы может вызвать метод трехцветной маркировки?
Согласно анализу алгоритма достижимости, если вы хотите найти уцелевший объект, вам нужно пройти от корней GC, а затем найти, достижим ли каждый объект.Если объект достижим, это уцелевший объект.Был ли осуществлен доступ к объекту и его ссылкамЭто условие будет разделено на следующие три цвета:
- Белый: Белый указывает на объекты, к которым не было доступа во время обхода корней GC. Появление белого цвета, очевидно, в начале анализа достижимости. В это время все объекты белые. Если анализ заканчивается, они все еще белые .объект, то это означает, что он недоступен и может быть переработан.
- Серый: Серый указывает на то, что доступ к объекту был осуществлен, но ссылка на этот объект не была доступна.
- Черный: черный означает, что доступ к этому объекту был осуществлен, а также доступ к ссылке на этот объект.
Примечание. Если объект по-прежнему остается белым после окончания метки, это означает, что объект «не найден» и на него нельзя ссылаться повторно.
Современные сборщики мусора почти все заимствуют идею алгоритма трехцветной маркировки, хотя способ реализации разный: например, белые/черные коллекции вообще не отображаются (но есть другие места для отражения цветов), серые коллекции могут быть пройдены через журнал стека/очереди/кэша и т. д., а метод обхода может быть обходом по ширине/глубине и т. д.
Трехцветная нотация создает два вида проблем, каждая из которых возникает при обстоятельствах, обусловленныхПользовательская среда и сборщик работают параллельно. Когда пользовательский поток изменяет ссылочное отношение, а сборщик перерабатывает ссылочное отношение, это приведет к тому, что объект, который умер, будет помечен как живой.Если это произойдет, проблема невелика, и сборщик будет перезапущен следующим Одна волна сбора закончилась, но есть и случай пометки уцелевших объектов как мертвых, что может иметь непредсказуемые последствия.
В ответ на исчезновение двух вышеуказанных объектов у отрасли есть два способа справиться с этим, один из нихИнкрементное обновление, одинСнимок в начале (SATB).
Не могли бы вы представить волну сборщиков мусора
Сборщик мусора является распространенным тестом на собеседованиях, а также обязательным пунктом проверки.Пока речь идет о проблемах, связанных с JVM, вокруг сборщика мусора будет делаться волна.Поэтому необходимо разбираться в этих сборщиках мусора.
Существует много сборщиков мусора.Сборщики мусора, предоставляемые разными поставщиками и версиями JVM, могут сильно различаться.В основном мы представляем сборщик мусора в виртуальной машине HotSpot.
Сборщик мусора — это конкретная реализация алгоритма сборки мусора.Как мы упоминали выше, алгоритм сборки мусора включает в себя маркировку-очистку, маркировку-сортировку и маркировку-копирование, поэтому соответствующие сборщики мусора также имеют разные реализации.
Мы знаем, что сборка мусора в виртуальной машине HotSpot собирается по поколениям, поэтому по разным поколениям сборщик мусора можно разделить на
Сборщики нового поколения: Serial, ParNew, Parallel Scavenge;
Сборщики старого поколения: Serial Old, Parallel Old, CMS;
Сборщик всей кучи: G1;
Серийный коллекционер
Последовательный сборщик мусора — это новое поколение сборщиков мусора.Это однопоточный сборщик, который использует алгоритм репликации для утилизации.Однопоточная работа означает не то, что есть только один сборщик мусора, а то, что сборщик работает. другие рабочие потоки должны быть приостановлены.Этот принудительный метод приостановкиStop The World, Сериал подобен олигополии, пока он говорит, все остальные младшие братья (потоки) должны уступить ему место. Принципиальная схема последовательного коллектора выглядит следующим образом:
Глобальная безопасная точка SefePoint: это особая позиция в коде.После того, как все пользовательские потоки достигают SafePoint, пользовательские потоки приостанавливаются, а поток GC очищается.
Хотя у Serial есть очевидные недостатки STW, с другой точки зрения, Serial все же очень приятен.У него также есть преимущества перед другими сборщиками, то есть он прост и эффективен.Для среды, где ресурсы памяти стоят на первом месте, это наименьшее дополнительное потребление памяти среди всех сборщиков.Для одноядерного процессора или среды с меньшим количеством процессорных ядер у сборщика Serial нет накладных расходов на взаимодействие потоков, поэтому эффективность Serial для концентрации на сборке мусора относительно высока.
Коллекционер ParNew
ParNew — многопоточная версия Serial.За исключением одновременного использования нескольких потоков, другие параметры и механизмы (STW, стратегия повторного использования, правила размещения объектов) точно такие же, как у Serial.Схема сборщика ParNew выглядит как следует:
Хотя ParNew использует несколько потоков для сборки мусора, он определенно не более эффективен, чем сборка Serial в однопоточной среде, потому что в многопоточности возникают накладные расходы на взаимодействие потоков, но по мере увеличения числа доступных ядер ЦП обработка ParNew Эффективность будет более эффективной, чем Serial.
Параллельный сборщик мусора
Сборщик Parallel Scavenge также является сборщиком нового поколения.Он также реализован на основе алгоритма маркировки-копирования, и он также может собирать параллельно.Таким образом, на поверхности Parallel Scavenge очень похож на ParNew, так что разница между ними?шерстяное сукно?
В центре внимания Parallel Scavenge в основномдостичь управляемой пропускной способности выше. Пропускная способность — это отношение времени, которое процессор тратит на выполнение пользовательского кода, к общему времени, потребляемому процессором. то есть
Вот пример пропускной способности: если время выполнения пользовательского кода + время запуска сборки мусора занимает в сумме 100 минут, а сборка мусора занимает 1 минуту, то пропускная способность составляет 99%. Чем короче время паузы, тем лучше она подходит для взаимодействия с пользователями или обеспечения качества ответа службы.Хорошая скорость ответа может улучшить взаимодействие с пользователем, а высокая пропускная способность может наиболее эффективно использовать ресурсы процессора.
Серийный старый коллекционер
Serial был введен ранее, мы знаем, что это новое поколение сборки мусора, использующее алгоритм пометки-копии. Сборщик Serial Old — это старая версия Serial, которая также является однопоточным сборщиком, использующимОтметить-организоватьАлгоритм сбора данных Serial Old имеет две цели: одна используется вместе с сборщиком Parallel Scavenge в JDK 5 и более ранних версиях, а другая используется в качествеCMS
Альтернатива сборщику, сборщику мусора CMS, скажем ниже, процесс сбора Serial Old выглядит следующим образом
Параллельный старый коллектор
Ранее мы представили сборщик Parallel Scavenge. Теперь давайте представим сборщик Parallel Old. Это более старая версия Parallel Scavenge, которая поддерживает многопотоковый параллельный сбор и реализована на основе алгоритма сортировки по меткам.После JDK 6 пропускная способность приоритетом можно считать Parallel Комбинация Scavenge + Parallel Old
Сборщик CMS
CMS
Основная цель сборщика - получить кратчайшее время паузы сбора, его полное названиеConcurrent Mark Sweep, как видно из названия, этот сборщик основан на алгоритме маркировки-развертки и поддерживает параллельный сбор.Его процесс работы немного сложнее, чем у сборщика, о котором мы упоминали выше.Его рабочий процесс выглядит следующим образом:
- Начальная отметка (начальная отметка CMS)
- Параллельная отметка (одновременная отметка CMS)
- Замечание (замечание CMS)
- Параллельная проверка (одновременная проверка CMS)
Для вышеуказанных четырех шагов требуется как начальная оценка, так и параллельная оценка.Stop The World, первоначальная маркировка предназначена только для маркировки объектов, непосредственно связанных с корнями GC, что происходит быстрее; фаза параллельной маркировки — это процесс обхода всего графа объектов из непосредственно связанных объектов корней GC. Этот процесс занимает много времени, но не требует приостановки пользовательского потока, то есть выполняется одновременно с потоком сборки мусора. В процессе одновременной маркировки могут быть ошибки или пропущенные маркировки.В это время необходимо перемаркировать и, наконец, этап параллельной очистки, чтобы очистить объекты, которые считаются погибшими на этапе маркировки.
Процесс сбора CMS выглядит следующим образом
CMS — очень хороший сборщик мусора, но идеальных сборщиков не бывает, и CMS такая же, у CMS как минимум три недостатка:
-
CMS очень чувствительна к ресурсам процессора, на параллельной стадии, хотя пользовательский поток и не будет поставлен на паузу, это приведет к замедлению работы приложения и уменьшению общей пропускной способности из-за занятия части потока.
-
CMS не может обработатьплавающий мусор, может бытьConcurrent Mode Failureпотерпеть неудачу и привести к другому полномуStop The WorldизFull GCпроизводить.
Что такое плавающий мусор? Из-за параллельной маркировки и параллельной фазы очистки пользовательский поток продолжает выполняться, поэтому программа естественно будет появляться с новым мусором, причем эта часть мусора появляется уже после окончания маркировки, и CMS не может обработать этот мусор, поэтому он может только дождаться следующей очистки во время сборки мусора. Эта часть мусора называется плавающим мусором.
-
Последним недостатком CMS является распространенная проблема concurrency-clearing, то есть будет много фрагментов пространства, что затруднит выделение больших объектов.
Первый сборщик мусора
Garbage First также известен какколлектор G1, его появление означает, что сборщик мусора прошел веху, почему это веха? Поскольку сборщик G1 является локально ориентированным сборщиком мусора, команда HotSpot разработала этот сборщик мусора для замены сборщика CMS, поэтому позже, после выпуска JDK 9, G1 заменил комбинацию Parallel Scavenge + Parallel Old и стал сборщиком мусора по умолчанию. на стороне сервера, и CMS больше не рекомендуется.
Предыдущие сборщики мусора имели ограничения по очищаемой области, потому что целевым диапазоном этих сборщиков мусора было либо все молодое поколение, либо все старое поколение, либо вся куча Java (Full GC), и G1 выскочил из этих рамок, Он может быть составлен для любой части кучи памятиКоллекция (Коллекция Набор, CSet), генерация которого больше не измеряется сборкой мусора, которая является G1Mixed GCмодель.
G1 восстанавливается на основе региона. Регион представляет собой произвольную компоновку в куче памяти. Каждый регион может играть пространство Эдема, пространство выжившего или пространство старости по мере необходимости. Сборщик может использовать разные стратегии для разных ролей региона. В Регионе также есть особая область, котораяHumongousRegion, который специально используется для хранения больших объектов, G1 считает, что если его размер превышает половину вместимости региона, он может быть определен как крупный объект. Большие объекты, которые превышают вместимость региона, будут храниться в смежных Humongous Regions, и большинство поведений G1 будут относиться к Humongous Region как к старому поколению.
G1 сохраняет концепцию нового поколения (Eden Suvivor) и старого поколения, но новое поколение и старое поколение больше не фиксированы. Оба они являются динамическими коллекциями ряда регионов.
Работу коллектора G1 можно разделить на следующие четыре этапа:
- Начальная маркировка: этот шаг предназначен только для маркировки объектов, с которыми GC Roots может напрямую ассоциироваться, и изменения значения указателя TAMS (каждый регион имеет два указателя RAMS), чтобы при одновременном запуске пользователем на следующем этапе он мог использоваться, когда он доступен. Объект размещен в регионе, на этом этапе необходимо приостановить пользовательский поток, но времени очень мало. Эта пауза делается при заимствовании Minor GC, поэтому ее можно игнорировать.
- Параллельная маркировка: из корня GC выполняется анализ достижимости объектов в куче, и граф объектов во всей куче рекурсивно сканируется, чтобы найти объекты, подлежащие повторному использованию. Когда сканирование графа объектов будет завершено, повторно обработайте объекты, записанные SATB, на которые есть ссылки во время параллелизма;
- Финальная отметка: короткая пауза в пользовательском потоке для обработки небольшой суммы, оставшейся после завершения параллельной фазы.SATBзапись (разновидность необработанного снимка, используемого для записи некоторых объектов в параллельной метке)
- Скрининг и переработка: отвечает за обновление статистических данных регионов, сортировку стоимости переработки и стоимости каждого региона, формулирование плана утилизации в соответствии с ожидаемым пользователем временем паузы, вы можете свободно выбирать несколько регионов для формирования коллекции утилизации и затем определить тот, который подлежит переработке.Часть уцелевших объектов Региона копируется в пустой Регион, а затем зачищается все пространство всего старого Региона. Операция здесь предназначена для перемещения объекта, поэтому пользовательский поток должен быть приостановлен и собран параллельно несколькими потоками-сборщиками.
Как видно из приведенных выше шагов, в дополнение к параллельной маркировке, остальные три этапа должны приостанавливать пользовательские потоки, поэтому этот сборщик G1 не является преследованием.низкая задержка, официальная цель дизайнаМаксимизируйте пропускную способность, сохраняя контроль над задержкой, как полнофункциональный сборщик.
Ниже приведена принципиальная схема переработки G1.
Коллектор G1 также имеет недостатки и проблемы:
- Первая проблема заключается в том, что в регионах существует проблема ссылок между поколениями Мы знаем, что можем использовать наборы памяти для решения проблемы ссылок между поколениями, но ссылки между поколениями в регионах намного сложнее;
- Второй вопрос: как обеспечить, чтобы поток сбора и поток пользователя работали, не мешая друг другу? CMS использует алгоритм инкрементного обновления, G1 использует исходный снимок (SATB), G1 выделяет два указателя TAMS на регион и делит часть пространства в регионе для размещения новых объектов в процессе одновременного повторного использования и одновременного повторного использования. объекта, выделенного центром времени, должен находиться над этими двумя положениями указателя. Если скорость восстановления памяти не соответствует скорости выделения памяти, сборщик G1 также заморозит выполнение пользовательских потоков, что приведет к полной сборке мусора и длительному STW.
- Третья проблема — невозможность моделировать предсказуемые паузы.
Введение в общие команды JVM
Ниже приведены некоторые часто используемые инструменты для настройки и устранения неполадок в JVM.
- jps: Инструмент обработки виртуальной машины, полное названиеJVM Process Status Tool, его функциональность такая же, как и в LinuxpsТочно так же вы можете перечислить запущенные процессы виртуальной машины и отобразить основной класс выполнения виртуальной машины.Main ClassУникальный идентификатор локальной виртуальной машины, на которой она расположена.Хотя функция относительно проста, эта команда определенно является наиболее часто используемой командой.
- jstat: Инструмент статистики виртуальных машин, инструмент командной строки для мониторинга информации о различных рабочих состояниях виртуальных машин.Он может отображать данные времени выполнения, такие как загрузка классов, память, сборка мусора и своевременная компиляция на локальной или удаленной виртуальной машине. процессы.
- jinfo: Инструмент информации о конфигурации Java, полное названиеConfiguration Info for Java, его функция заключается в настройке различных параметров виртуальной машины.
- jmap: инструмент сопоставления памяти Java, полное названиеMemory Map For Java, который используется для создания моментальных снимков дампа для устранения неполадок с использованием памяти.
- jhat: Инструмент анализа моментальных снимков дампа кучи виртуальной машины, полное названиеJVM Heap Analysis Tool, эта команда обычно используется вместе с jmap, jhat имеет встроенный HTTP/веб-сервер, который можно просмотреть в браузере после создания моментального снимка дампа. Однако команда jmap обычно используется чаще.
- jstack: Инструмент трассировки стека Java, полное имяStack Trace for Java, как следует из названия, эта команда используется для отслеживания использования стека, а также для моментального снимка потока виртуальной машины в текущий момент.Снимок потока представляет собой набор каждого выполняемого стека методов в текущем виртуальном машина.
Какова модель родительского делегирования?
Загрузка класса JVM по умолчанию для использованияМодель родительского делегирования, так что же такое родительская модель делегирования?
Здесь нам нужно сначала ввести три загрузчика классов:
- запустить загрузчик классов,Bootstrap Class Loader, этот загрузчик классов реализован на C++, он является частью JVM, этот загрузчик классов отвечает за загрузку<JAVA_HOME>\libкаталог, программа Java не может напрямую ссылаться на загрузчик класса запуска. Другими словами, загрузка общих классов в JDK выполняется загрузчиком классов запуска.
- загрузчик класса расширения,Extension Class Loader, этот загрузчик классов реализован на Java, он отвечает за загрузку<JAVA_HOME>\lib\extсодержание.
- загрузчик классов приложений,Application Class Loader, этот загрузчик классов созданsum.misc.Launcher$AppClassLoaderдобиться, он отвечает за загрузкуClassPathДля всех вышеперечисленных библиотек классов, если приложение не определяет собственный загрузчик классов, этот загрузчик классов используется по умолчанию.
Таким образом, наши приложения Java дополняются этими тремя загрузчиками классов.Конечно, пользователи также могут определять свои собственные загрузчики классов, а именноUser Class Loader, модели этих загрузчиков классов следующие
Вышеупомянутые загрузчики классов образуют разные иерархии.Когда нам нужно загрузить класс, загрузчик дочернего класса не загружает его сразу, а будет запрашивать загрузчик родительского класса по очереди, вплоть до самого высокого уровня. загрузчик классов. Если загрузчик класса запуска не может быть загружен, пусть загрузчик дочернего класса загрузит его по очереди. Это модель родительского делегирования.
Недостатки модели родительского делегирования?
В модели родительского делегирования загрузчик дочернего класса может использовать классы, уже загруженные загрузчиком родительского класса, но загрузчик родительского класса не может использовать классы, уже загруженные загрузчиком дочернего класса. Это приводит к тому, что модель родительского делегирования не решает всех проблем с загрузчиком классов.
Java предоставляет множество внешних интерфейсов, которые в совокупности называютсяService Provider Interface, SPI, что позволяет третьим сторонам реализовывать эти интерфейсы, но эти интерфейсы предоставляются базовыми классами Java и загружаются загрузчиком классов Bootstrap, в то время как общие расширенные интерфейсы загружаются загрузчиком классов приложений, загрузчик классов Bootstrap не может найти класс реализации SPI, потому что он только загружает основную библиотеку Java. Он также не может проксировать загрузчик классов приложений, потому что это самый верхний загрузчик классов.
Три нарушения механизма родительского делегирования
Хотя механизм родительского делегирования является реализацией загрузчика классов, который Java настоятельно рекомендует разработчикам, это не обязательно, что вы должны реализовать его таким образом, поэтому он также поврежден.На самом деле, всего было три случая где нарушен механизм родительского делегирования:
- Первое разрушение родительского механизма делегирования произошло до появления родительского механизма делегирования. Поскольку родительский механизм делегирования был введен после JDK 1.2, концепция загрузки классов существовала, когда впервые появилась Java. Поэтому, прежде чем обратиться к родительскому механизму делегирования, дизайн Разработчики должны учитывать код некоторых загрузчиков классов, настроенных разработчиками, поэтому после JDK 1.2 был добавлен новый java.lang.ClassLoader.findClassметод, который помогает пользователям переопределить этот метод findClass при написании логики загрузчика классов, а не на основеloadClassнаписать.
- Второй раз механизм родительского делегирования нарушается из-за его собственной модели, потому что он может быть загружен только вверх (базовый), а более простые классы загружаются верхним загрузчиком, поэтому, если базовый тип хочет вызвать пользовательский код , Как сделать? Это механизм SPI, о котором мы упоминали в вопросе выше. Так как же команда JDK делает это? они ссылаются наClassLoader контекста потока, к этому загрузчику классов можно получить доступ через класс java.lang.ThreadsetContextClassLoaderУстановите, если поток не был установлен при его создании, он будет унаследован от родительского потока.Если загрузчик класса не установлен глобально, этот ClassLoader является загрузчиком класса по умолчанию. Хотя такое поведение является неприемлемым, код Java вJNDI, JDBCи т.д. все делается таким образом. Вплоть до JDK 6 со ссылкой наjava.util.ServiceLoader,использоватьMETA-INF/services+ Шаблон проектирования цепочки ответственности решает этот механизм загрузки SPI.
- В третий раз нарушается механизм родительского делегирования из-за введения горячей загрузки и горячего развертывания из-за динамических требований пользователей к программам. В связи с изменениями времени мы надеемся, что Java может обеспечить горячее развертывание, такое как мышь и клавиатура, мгновенная загрузка (класс загрузки) и введение OSGI.Ключ к горячему развертыванию OSGI заключается в реализации его собственного механизма загрузчика классов. .BundleТо есть у модулей есть свой загрузчик классов. Когда Bundle необходимо заменить, горячую загрузку можно реализовать путем непосредственной замены Bundle вместе с загрузчиком классов. В среде OSGI загрузчик классов больше не подчиняется родительскому механизму делегирования, а использует более сложный механизм загрузки.
Каковы общие параметры настройки JVM?
- -Xms256m: инициализировать размер кучи до 256 м;
- -Xmx2g: максимальный размер кучи составляет 2 г;
- -Xmn50m: Размер нового поколения 50м;
- -XX:+PrintGCDetails распечатать детали сборщика мусора;
- -XX:+HeapDumpOnOutOfMemoryError При возникновении ошибки OutOfMemoryError создать дамп моментального снимка кучи;
- -XX:NewRatio=4 Установить соотношение молодой и старой памяти 1:4;
- -XX:SurvivorRatio=8 Установить соотношение Эдема и Выжившего в новом поколении на 8:2;
- -XX:+UseSerialGC Использовать серийный сборщик как для молодого, так и для старого поколения Serial + Serial Old
- -XX:+UseParNewGC указывает на использование комбинации сборщика мусора ParNew + Serial Old;
- -XX:+UseParallelGC Использовать Parallel Scavenge для молодого поколения и Serial Old для старого поколения
- -XX:+UseParallelOldGC: комбинация нового поколения ParallelScavenge + старого поколения ParallelOld;
- -XX:+UseConcMarkSweepGC: новое поколение использует ParNew, а старое поколение использует CMS;
- -XX:NewSize: минимальное значение нового поколения;
- -XX:MaxNewSize: максимальное значение нового поколения
- -XX:MetaspaceSize Размер инициализации метапространства
- -XX:MaxMetaspaceSize максимальное метапространство
Если вам будет полезно, можете обратить внимание на официальный аккаунт: программист cxuan, там вас ждут более хардкорные статьи.