Перед тем, как мы начнем знакомить с CMS и G1, можно пропустить несколько моментов:
- В зависимости от особенностей разных поколений коллекторы могут быть разными. Некоторые коллекторы можно использовать как для молодого, так и для старого поколения, а в других случаях необходимо подобрать соответствующий коллектор для молодого или старого поколения соответственно. Вообще говоря, частота сбора коллектора нового поколения высока, и следует выбирать коллектор с высокой производительностью; в то время как частота сбора коллектора старого поколения относительно мала, что более чувствительно к пространству, и коллектор на основе следует избегать алгоритма репликации.
- В момент, когда производится сборка мусора, приложение нужно приостановить.
- Его можно собирать последовательно или параллельно.
- Если вы можете делать параллельные коллекции (приложению не нужно приостанавливать работу), это было бы здорово.
- Если поведение коллекции управляемо, это тоже замечательно.
CMS и G1 — большие убийцы в сборщике мусора, их нужно хорошо понимать, и их часто спрашивают на собеседованиях.
Я надеюсь, что вы прочитаете со следующими вопросами, прочитаете с целью и получите больше:
- Почему нет отличного коллектора, который подходит для всех сценариев, как серебряная пуля?
- Преимущества, недостатки и применимые сценарии CMS?
- Почему CMS можно использовать только как сборщик старого поколения и нельзя использовать для сбора молодого поколения?
- Преимущества, недостатки и применимые сценарии G1?
1 сборщик CMS
Сборщик CMS (Concurrent Mark Sweep) — это сборщик, целью которого является получение кратчайшего времени паузы восстановления. Это связано с тем, что при работе сборщика CMS рабочий поток GC и пользовательский поток могут并发
Выполните, чтобы достичь цели сокращения времени паузы сбора.
Сборщик CMS действует только настаростьколлекция основана на标记-清除算法
, процесс его работы делится на 4 этапа:
- Начальная отметка (начальная отметка CMS)
- Параллельная отметка (одновременная отметка CMS)
- Замечание (замечание CMS)
- Параллельная проверка (одновременная проверка CMS)
в,初始标记
,重新标记
Эти два шага по-прежнему требуют Stop-the-world.Начальная метка предназначена только для пометки объектов, с которыми GC Roots может напрямую ассоциироваться, что очень быстро.Фаза параллельной маркировки — это процесс трассировки GC Roots, а фаза примечания — для исправления генерации метки, вызванной продолжением работы пользовательской программы. для работы во время одновременной маркировки Отмеченная запись измененной части объекта, время паузы на этом этапе, как правило, несколько больше, чем на начальной стадии, но значительно короче, чем время одновременной маркировки.
CMS разделяет цикл сбора данных конвейерным способом, сохраняя длительные операции, выполняющиеся одновременно с потоками приложения. Выделяются только те операционные блоки, которые должны быть выполнены STW, и эти блоки контролируются для запуска в нужное время и могут быть гарантированно завершены за короткое время. Таким образом, в течение всего цикла сбора толькоДве короткие паузы (начальная маркировка и повторная маркировка),Достижение цели приблизительного параллелизма.
Сборщик CMSпреимущество: Параллельный сбор, низкая пауза.
Сборщик CMSнедостаток:
- Сборщик CMS очень чувствителен к ресурсам ЦП.
- Сборщик CMS не может обрабатывать плавающий мусор.
- Коллектор CMS основан на алгоритме пометки и очистки, который имеет свои недостатки.
Фундаментальная причина, по которой сборщик CMS может быть параллельным, заключается в том, чтоАлгоритм, основанный на «маркировке-развертке», принят, и процесс алгоритма разложен на мелкие детали.. В предыдущей главе алгоритм пометки-зачистки будет генерировать много фрагментации памяти, что неприемлемо для молодого поколения, поэтому сборщик нового поколения не предоставляет версию CMS.
Кроме того, следует добавить, что когда JVM приостанавливается, ей нужно выбрать тайминг. Из-за сложности системы JVM во время работы ее нельзя приостановить в любой момент, поэтому вводится понятие безопасной точки.
Сейфпойнт
Безопасная точка, то есть выполнение программы не останавливается везде для запуска GC, а только при достижении безопасной точки. Выбор точек сохранения не должен быть настолько мал, чтобы сборщик мусора слишком долго ждал, и не должен быть слишком частым, чтобы перегружать нагрузку во время выполнения.
Первоначальная цель точки сохранения — не остановить другие потоки, а найти стабильное состояние выполнения. В этом состоянии выполнения стек виртуальной машины Java не изменяется. Это позволяет сборщику мусора «безопасно» выполнять анализ достижимости. Пока он не покидает эту безопасную точку, виртуальная машина Java может продолжать выполнять этот собственный код, пока выполняется сборка мусора.
Во время работы программы нельзя везде делать паузы для запуска сборщика мусора, только когда он достигает безопасной точки. Выбор точки безопасности в основном основан на критерии «имеет ли программа функцию, позволяющую выполнять программу в течение длительного времени». "долгое исполнение«Наиболее очевидная особенность — это мультиплексирование последовательностей инструкций, таких как вызовы методов, переходы по циклам, переходы по исключениям и т. д., поэтому инструкции с этими функциями будут генерировать точки безопасности.
Что касается точек сохранения, следует рассмотреть еще одну проблему: как заставить все потоки (не включая потоки, выполняющие вызовы JNI) «запускаться» в ближайшую точку сохранения, а затем останавливаться, когда происходит сборка мусора.
Два решения:
-
Превентивное отстранение
Упреждающее прерывание не требует активного взаимодействия исполняемого кода потока. Когда происходит GC, сначала прерываются все потоки. Если обнаруживается, что место прерывания потока не находится в безопасной точке, поток восстанавливается и "бежит" в безопасную точку. . В настоящее время немногие виртуальные машины используют этот метод для приостановки потоков в ответ на события GC.
-
Добровольное отстранение
Идея активного прерывания заключается в том, что когда GC нужно прервать поток, он не работает с потоком напрямую, а просто устанавливает флаг.При выполнении каждого потока он активно опрашивает флаг, и когда обнаруживает, что флаг прерывания истинен, он прерывает и приостанавливает себя. Место, где флаг опроса совпадает с безопасной точкой, плюс место, где создание объекта требует выделения памяти.
безопасный район
Ссылается на фрагмент кода, отношение ссылки не изменится. Безопасно начинать GC в любом месте в этой области. Безопасные регионы также можно рассматривать как расширенные точки безопасности.
2 коллектор G1
G1 переопределяет пространство кучи, ломает исходную модель генерации и делит кучу на регионы. Цель этого состоит в том, что сбор не должен выполняться для всей кучи, что является его наиболее примечательной особенностью. Преимуществом зонирования является модель сбора с предсказуемым временем паузы: пользователь может указать, сколько времени должно занять выполнение операции сбора. То есть G1 обеспечивает характеристики сбора в режиме, близком к реальному времени.
Характеристики G1 и CMS сравниваются следующим образом:
особенность | G1 | CMS |
---|---|---|
Параллелизм и генерация | да | да |
Максимально увеличить свободную память кучи | да | нет |
низкая задержка | да | да |
пропускная способность | высокий | Низкий |
уплотнение | да | нет |
предсказуемость | мощный | слабый |
Физическое разделение молодых и старых | нет | да |
G1 имеет следующие особенности:
- Параллелизм и параллелизм: G1 может в полной мере использовать аппаратные преимущества в многопроцессорной и многоядерной среде и использовать несколько ЦП для сокращения времени паузы Stop-the-world.Некоторым другим сборщикам изначально необходимо приостанавливать операции GC, выполняемые потоками Java. , Коллектор G1 еще может пройтипараллелизмспособ, чтобы программа Java продолжала работать.
- Коллекция поколений
- Пространственная интеграция: в отличие от алгоритма маркировки-развертки CMS, G1 в целом основан наалгоритм маркировки-сопоставленияРеализованный сборщик, с локальной (между двумя регионами) точки зрения, основан на "копировать«Алгоритм достижения. Но в любом случае эти два алгоритма означают, что G1 не будет генерировать фрагментацию пространства памяти во время работы и может предоставить обычную полезную память после сбора.Эта функция удобна для долгой работы программы: при размещении больших объектов следующая сборка мусора не будет запускаться заранее, так как невозможно найти непрерывное пространство памяти..
- Предсказуемые паузы: это преимущество G1 по сравнению с CMS, и сокращение времени пауз является общей задачей как G1, так и CMS.
Другие коллекционеры до G1 собирали все молодое или старое поколение, чего уже не происходит с G1. При проектировании структуры кучи G1 ломает предыдущую модель фиксирования диапазона сбора в молодом или старом поколении.G1 делит кучу на множество единиц площади одинакового размера, и каждая единица называется Region. Регион представляет собой пространство памяти с непрерывными адресами Состав модуля G1 показан на следующем рисунке:
Сборщик G1 делит всю кучу Java на несколько независимых областей (Regions) одинакового размера.Хотя концепции нового поколения и старого поколения все еще сохраняются, новое поколение и старое поколение больше не изолированы физически, они все часть набора регионов (которые не обязательно должны быть непрерывными). Размер региона тот же. Значение представляет собой степень 2 от 1 М до 32 МБ байт. JVM попытается разделить около 2048 регионов одинакового размера. См. следующее.исходный код. На самом деле это число можно настроить вручную, а G1 автоматически подстроится под размер кучи.
#ifndef SHARE_VM_GC_G1_HEAPREGIONBOUNDS_HPP
#define SHARE_VM_GC_G1_HEAPREGIONBOUNDS_HPP
#include "memory/allocation.hpp"
class HeapRegionBounds : public AllStatic {
private:
// Minimum region size; we won't go lower than that.
// We might want to decrease this in the future, to deal with small
// heaps a bit more efficiently.
static const size_t MIN_REGION_SIZE = 1024 * 1024;
// Maximum region size; we don't go higher than that. There's a good
// reason for having an upper bound. We don't want regions to get too
// large, otherwise cleanup's effectiveness would decrease as there
// will be fewer opportunities to find totally empty regions after
// marking.
static const size_t MAX_REGION_SIZE = 32 * 1024 * 1024;
// The automatic region size calculation will try to have around this
// many regions in the heap (based on the min heap size).
static const size_t TARGET_REGION_NUMBER = 2048;
public:
static inline size_t min_size();
static inline size_t max_size();
static inline size_t target_number();
};
#endif // SHARE_VM_GC_G1_HEAPREGIONBOUNDS_HPP
Сборщик G1 может моделировать предсказуемое время паузы, потому что он может программно избегать сборки мусора всей области во всей куче Java.. G1会通过一个合理的计算模型,计算出每个Region的收集成本并量化,这样一来,收集器在给定了“停顿”时间限制的情况下,总是能选择一组恰当的Regions作为收集目标,让其收集开销满足这个限制条件,以此达到实时收集的目的。
Для приложений, планирующих миграцию с CMS или коллектора ParallelOld, следуйтеофициальныйЕсли вы обнаружите, что он соответствует следующим характеристикам, вы можете рассмотреть возможность замены его коллектором G1 для повышения производительности:
- Данные реального времени занимают более половины пространства кучи;
- Существенные изменения скорости выделения или «раскрутки» объекта;
- Ожидайте устранения длинных GC или пауз (более 0,5–1 секунды).
Оригинальный текст выглядит следующим образом: Приложения, работающие сегодня либо с CMS, либо со сборщиком мусора ParallelOld, выиграют от перехода на G1, если приложение имеет одну или несколько из следующих особенностей.
- More than 50% of the Java heap is occupied with live data.
- The rate of object allocation rate or promotion varies significantly.
- Undesired long garbage collection or compaction pauses (longer than 0.5 to 1 second)
Процесс работы со сбором G1 примерно выглядит следующим образом:
-
Начальная маркировка: Просто отметьте объекты, с которыми GC Roots может напрямую ассоциироваться, и измените значение TAMS (Next Top at Mark Start), чтобы при одновременном запуске пользовательской программы на следующем этапе новые объекты могли создаваться в правильных доступных регионах. .Этот этап требует
停顿线程
, но это занимает очень мало времени. - Параллельная маркировка: анализировать доступность объектов в куче, начиная с GC Roots, чтобы найти уцелевшие объекты,Этот этап занимает много времени, но может выполняться одновременно с пользовательской программой.
-
Окончательная маркировка: Чтобы исправить часть записи метки, которая изменяет метку из-за непрерывной работы программы пользователя во время одновременной метки, виртуальная машина записывает изменения объекта за этот период в журналы запомненных установок потока, а данные Журналы запомненного набора должны быть объединены на заключительном этапе маркировки с запомненным набором,Этот этап требует
停顿线程
, но может выполняться параллельно. - Скрининг и переработка (подсчет и эвакуация данных в реальном времени): сначала отсортируйте значение восстановления и стоимость каждого региона и сформулируйте план восстановления в соответствии с ожидаемым пользователем временем паузы GC. Этот этап также может выполняться одновременно с пользовательской программой, но, поскольку повторно используется только часть области Region, время контролируется пользователем, и приостановка пользовательского потока значительно повысит эффективность сбора.
Глобальные переменные и объекты, на которые есть ссылки в стеке, могут быть включены в корневой набор, чтобы при поиске мусора можно было сканировать пространство кучи из корневого набора. В G1 вводится новый тип, который можно добавить в корневой набор, а именно记忆集
(Запоминающийся набор). Запомненные наборы (также называемые RSets) используются для отслеживания ссылок на объекты. Большая часть открытого исходного кода G1 получена из запомненного набора, который, например, обычно составляет около 20% или более размера кучи. Более того, когда мы копируем объект, потому что нам нужно сканировать и изменять информацию Таблицы Карт, эта скорость влияет на скорость копирования, что в свою очередь влияет на время паузы.
Карточный стол
Существует сценарий, когда объекты в старом поколении могут ссылаться на объекты в молодом поколении, при маркировке уцелевших объектов необходимо сканировать все объекты в старом поколении. Поскольку объект содержит ссылку на объект молодого поколения, эта ссылка также называется корнями GC. Разве это не должно снова выполнить полное сканирование кучи? Стоимость слишком высока.
Решение, данное HotSpot, представляет собой программу под названием卡表
(карточный стол) технологии. Технология делит всю кучу на карты размером 512 байт и поддерживает таблицу карт для хранения идентификационного бита для каждой карты. Этот флаг указывает, может ли соответствующая карта иметь ссылку на объект нового поколения. Если возможно, то считаем карту грязной.
При выполнении Minor GC вместо сканирования всей старины мы можем искать грязные карты в таблице карт и добавлять объекты в грязных картах в GC Roots of Minor GC. После завершения сканирования всех грязных карт виртуальная машина Java очистит идентификационные биты всех грязных карт до нуля.
Чтобы гарантировать, что каждая карта, которая может иметь ссылку на объект нового поколения, помечена как грязная карта, виртуальная машина Java должна перехватить операцию записи каждой переменной экземпляра ссылки и выполнить соответствующую операцию флага записи.
Карточную таблицу можно использовать для уменьшения полного сканирования кучи старого поколения, что может значительно повысить эффективность GC..
Мы можем посмотреть на взгляд официального документа на G1 (это английское описание относительно простое, я не буду его переводить):
Future: G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector (CMS). Comparing G1 with CMS, there are differences that make G1 a better solution. One difference is that G1 is a compacting collector. G1 compacts sufficiently to completely avoid the use of fine-grained free lists for allocation, and instead relies on regions. This considerably simplifies parts of the collector, and mostly eliminates potential fragmentation issues. Also, G1 offers more predictable garbage collection pauses than the CMS collector, and allows users to specify desired pause targets.
3 Резюме
Проверил статьи Du Niang о G1, большинство статей, посвященных G1, застряли в JDK7 или более ранних реализациях, и многие выводы были предвзятыми, и даже некоторые прошлые варианты GC больше не рекомендуются. Например, журналы JVM и GC были реорганизованы в JDK9, например, PrintGCDetails был помечен как устаревший, а PrintGCDateStamps был удален, указав, что он приведет к сбою запуска JVM.
Большая часть введения CMS и G1 в этой статье также основана на JDK 7. В новой версии есть небольшое введение в содержание, но я не сделал много введения (я не проводил углубленного исследования новой версию JVM).
4 Ссылка
"Глубокое понимание виртуальной машины Java" «Горячая точка в действии» "Колонка времени гиков"