Виды сборщиков мусора (HotSpot)¶
Note
-verbose:gc Включает режим логирования сборок мусора в stdout.
Serial (последовательный) GC¶
Самый простой вариант для приложений с небольшим объемом данных и не требовательных к задержкам. Редко когда используется, но на слабых компьютерах может быть выбран виртуальной машиной в качестве сборщика по умолчанию.
В начале каждой сборки мусора работа основных потоков приложения останавливается и возобновляется только после окончания сборки (minor - full). Причем всю работу по очистке Serial GC выполняет не торопясь, в одном потоке, последовательно, за что и удостоился своего имени.
Warning
По мере заполнения кучи данными JVM может не только проводить чистку памяти, но и запрашивать у ОС выделение дополнительной памяти для расширения регионов.
При нехватке памяти бросается исключение OutOfMemoryError Технически, это происходит в случае, если работа сборщика начинает занимать не менее 98% времени и при этом сборки мусора освобождают не более 2% памяти.
Разметка областей
-XX:+UseSerialGC
Ссылки:
Если приложению не требуется большой размер кучи для работы (Oracle указывает условную границу 100 МБ), оно не очень чувствительно к коротким остановкам и ему для работы доступно только одно ядро процессора, то можно приглядеться к этому варианту. В противном случае можно поискать вариант по-лучше.
Minor (Малая) GC¶
Note
Применяется для Serial/Paralel/CMS GC и частично, по смыслу для G1 GC.
Малые сборки выполняются достаточно часто и удаляют основную часть мертвых объектов.
Для целей малой сборки мусора объект считается мертвым и подлежащим утилизации, если до него невозможно добраться по ссылкам ни из объектов старшего поколения, ни из так называемых корней (roots), к каковым относятся ссылки из стеков потоков, статические члены классов и т. п.
- Механизм действия
Первым делом такая сборка находит и удаляет мертвые объекты из Eden. Оставшиеся живые объекты переносятся в пустой регион Survivor. Один из двух регионов Survivor всегда пустой, именно он выбирается для переноса объектов из Eden.
Но рано или поздно приложение опять займет всю область Eden и JVM снова попытается провести малую сборку, на этот раз очищая Eden и частично занятый Survivor 0, после чего перенося все выжившие объекты в пустой регион Survivor 1.
В следующий раз в качестве региона назначения опять будет выбран Survivor 0. Пока места в регионах Survivor достаточно, все идет хорошо.
JVM постоянно следит за тем, как долго объекты перемещаются между Survivor 0 и Survivor 1, и выбирает подходящий порог для количества таких перемещений, после которого объекты перемещаются в Tenured, то есть переходят в старшее поколение. Если регион Survivor оказывается заполненным, то объекты из него также отправляются в Tenured.
Full (Полная) GC¶
Note
Применяется для Serial/Paralel GC
Полные сборки выполняются тогда, когда текущий объем выделенной программе памяти близок к исчерпанию и малой сборкой уже не обойтись (места для новых объектов не хватает в Tenured).
При полной сборке мусора могут анализироваться оба поколения, поэтому здесь сборщик может плясать только от корней.
- Механизм действия
При этом старшее поколение не делится на подрегионы по аналогии с младшим, а представляет собой один большой кусок памяти, поэтому после удаления мертвых объектов из Tenured производится не перенос данных (переносить уже некуда), а их уплотнение, то есть размещение последовательно, без фрагментации.
Такой механизм очистки называется Mark-Sweep-Compact по названию его шагов (пометить выжившие объекты, очистить память от мертвых объектов, уплотнить выжившие объекты).
Parallel (параллельный) GC¶
Наследует подходы к сборке от Serial (последовательный) GC, но добавляет параллелизм в некоторые операции, а также возможности по автоматической подстройке под требуемые параметры производительности.
Tip
Для определения количества потоков, которые будут использоваться при сборке мусора, на компьютере с N ядрами процессора: if N<=8 then N else N*k(currentComputer) ~ N * 5/8 (-XX:ParallelGCThreads)
По умолчанию и малая и полная сборка задействуют многопоточность. Малая пользуется ею при переносе объектов в старшее поколение, а полная — при уплотнении данных в старшем поколении. Каждый поток сборщика получает свой участок памяти в регионе Old Gen, так называемый буфер повышения (promotion buffer), куда только он может переносить данные, чтобы не мешать другим потокам. Такой подход ускоряет сборку мусора, но имеет и небольшое негативное последствие в виде возможной фрагментации памяти. Отключить параллельные работы по уплотнению объектов в старшем поколении: -XX:-UseParallelOldGC
Как и в случае с последовательным сборщиком, на время операций по очистке памяти все основные потоки приложения останавливаются. Разница только в том, что пауза, как правило, короче за счет выполнения части работ в параллельном режиме.
К плюсам данного сборщика мусора можно отнести подстраивание параметров сборщика мусора под указанные параметры производительности
(Максимальная задержка -XX:MaxGCPauseMillis , Пропускная способность -XX:GCTimeRatio …).
Делается это за счет составления статистики предыдущих сборок и выявление динамики сборки мусора
(т.е. если при малой сборке JVM не удается укладываться в отведенное вами время, размер младшего поколения может быть уменьшен).
-XX:+UseParallelGC
Use parallel garbage collection for Young generation.
-XX:+UseParallelOldGC
Use parallel garbage collection for the full collections.
Enabling this option automatically sets -XX:+UseParallelGC.
~ Если на компьютере больше одного процессорного ядра и явно не указали, какой сборщик хотите использовать в программе, то почти наверняка JVM остановит свой выбор на Parallel GC.
Concurrent Mark Sweep (CMS) GC¶
Нацелен на снижение максимальных задержек путем выполнения части работ по сборке мусора параллельно с основными потоками приложения. Подходит для работы с относительно большими объемами данных в памяти.
Сборщик CMS получил свое название благодаря тому, что выполняет шаги Mark Sweep (пометка выживших объектов и удаление мертвых объектов) параллельно с работой основной программы
В отношении малой сборки мусора данный сборщик имеет такие же принципы как и Serial/Parallel. Так как подходы к организации памяти у CMS аналогичны используемым в Serial / Parallel GC, для него применимы те же опции определения размеров регионов кучи, а также опции автоматической подстройки под требуемые параметры производительности.
Важным отличием сборщика CMS от рассмотренных ранее является также то, что он не дожидается заполнения Tenured для того, чтобы начать старшую сборку (или при указании -XX:CMSInitiatingOccupancyFraction). Вместо этого он трудится в фоновом режиме постоянно, пытаясь поддерживать Tenured в компактном (не в смысле упорядоченном, не фрагментированном) состоянии.
Warning
Данный сборщик не уплотняет объекты в старшем поколении, что приводит к фрагментации Tenured. Этот факт в совокупности с наличием плавающего мусора приводит к необходимости выделять приложению (конкретно — старшему поколению) больше памяти, чем потребовалось бы для других сборщиков (Oracle советует на 20% больше).
Сборщик CMS достаточно интеллектуальный. Например, он старается разносить во времени малые и старшие сборки мусора, чтобы они совместно не создавали продолжительных пауз в работе приложения.
Достоинством данного сборщика по сравнению с Serial / Parallel GC является его ориентированность на минимизацию времен простоя, что является критическим фактором для многих приложений. Но для выполнения этой задачи приходится жертвовать ресурсами процессора и зачастую общей пропускной способностью.
-XX:+UseConcMarkSweepGC
Удален в (Java 14)
Major (Старшая) GC¶
В случае CMS full сборку называют старшей (major) сборкой, а не полной, так как она не затрагивает объекты младшего поколения. В результате, малая и старшая сборки здесь всегда разделены. Одним из побочных эффектов такого разделения является то, что все объекты младшего поколения (даже потенциально мертвые) могут играть роль корней при определении статуса объектов в старшем поколении.
- Механизм действия
Остановка основных потоков приложения и пометка всех объектов, напрямую доступных из корней.
Приложение возобновляет свою работу, а сборщик параллельно с ним производит поиск всех живых объектов доступных по ссылкам из тех самых помеченных корневых объектов (эту часть он делает в одном или в нескольких потоках)
Сборщик еще раз приостанавливает работу приложения и просматривает кучу для поиска живых объектов, ускользнувших от него за время первого прохода.
При этом допускается, что в живые будут записаны объекты, которые на время окончания составления списка таковыми уже не являются. Эти объекты называются плавающим мусором (floating garbage), они будут удалены в процессе следующей сборки.
После того, как живые объекты помечены, работа основных потоков приложения возобновляется, а сборщик производит очистку памяти от мертвых объектов в нескольких параллельных потоках. После очистки не производится упаковка объектов в старшем поколении.
Отдельно следует рассмотреть ситуацию, когда сборщик не успевает очистить Tenured до того момента, как память полностью заканчивается. В этом случае работа приложения останавливается, и вся сборка производится в последовательном режиме. Такая ситуация называется сбоем конкурентного режима.
Механизм действия
Garbage-First (G1) GC¶
Создан для постепенной замены CMS, особенно в серверных приложениях, работающих на многопроцессорных серверах и оперирующих большими объемами данных.
-XX:+UseG1GC
Ссылки:
Изменение подхода к организации кучи¶
Здесь память разбивается на множество регионов одинакового размера.
Размер этих регионов зависит от общего размера кучи и по умолчанию выбирается так, чтобы их было не больше 2048, обычно получается от 1 до 32 МБ. Исключение составляют только так называемые громадные (humongous) регионы, которые создаются объединением обычных регионов для размещения очень больших объектов.
Разделение регионов на Eden, Survivor и Tenured в данном случае логическое, регионы одного поколения не обязаны идти подряд и даже могут менять свою принадлежность к тому или иному поколению.
Опираясь на статистику о предыдущих сборках, G1 может менять количество регионов, закрепленных за определенным поколением, для оптимизации будущих сборок.
Малые сборки мусора¶
Малые сборки выполняются периодически для очистки младшего поколения и переноса объектов в регионы Survivor, либо их повышения до старшего поколения с переносом в Tenured.
Над переносом объектов трудятся несколько потоков, и на время этого процесса работа основного приложения останавливается.
Очистка выполняется не на всем поколении, а только на части регионов, которые сборщик сможет очистить не превышая желаемого времени.
При этом он выбирает для очистки те регионы, в которых, по его мнению, скопилось наибольшее количество мусора и очистка которых принесет наибольший результат. Отсюда как раз название Garbage First — мусор в первую очередь.
Marking cycle¶
В G1 существует процесс, называемый циклом пометки (marking cycle), который работает параллельно с основным приложением и составляет список живых объектов.
- Механизм действия (первые три пункта такие же, как и в Concurrent Mark Sweep (CMS) GC)
Initial mark. Пометка корней (с остановкой основного приложения) с использованием информации, полученной из малых сборок.
Concurrent marking. Пометка всех живых объектов в куче в нескольких потоках, параллельно с работой основного приложения.
Remark. Дополнительный поиск не учтенных ранее живых объектов (с остановкой основного приложения).
Cleanup. Очистка вспомогательных структур учета ссылок на объекты и поиск пустых регионов, которые уже можно использовать для размещения новых объектов. Первая часть этого шага выполняется при остановленном основном приложении.
Для получения списка живых объектов G1 использует алгоритм Snapshot-At-The-Beginning (SATB), то есть в список живых попадают все объекты, которые были таковыми на момент начала работы алгоритма, плюс все объекты, созданные за время его выполнения.
Смешанные сборки¶
После окончания цикла пометки G1 переключается на выполнение смешанных сборок.
При каждой сборке к набору регионов младшего поколения, подлежащих очистке, добавляется некоторое количество регионов старшего поколения.
Количество таких сборок и количество очищаемых регионов старшего поколения выбирается исходя из имеющейся у сборщика статистики о предыдущих сборках таким образом, чтобы не выходить за требуемое время сборки.
Может оказаться так, что в процессе очистки памяти в куче не остается свободных регионов, в которые можно было бы копировать выжившие объекты. В таком случае сборщик выполняет полную сборку мусора по всей куче при остановленных основных потоках приложения.
Как только сборщик очистил достаточно памяти, он переключается обратно в режим малых сборок. Смешанные сборки будут запущены тогда, когда заполненность кучи превысит определенный порог.
- Громадные объекты (любой объект размером больше половины региона)
никогда не перемещается между регионами
может удаляться в рамках цикла пометки или полной сборки мусора
в регион, занятый громадным объектом, больше никого не подселяют, даже если в нем остается свободное место
Достоинства и недостатки¶
В целом считается, что сборщик G1 более аккуратно предсказывает размеры пауз, чем CMS, и лучше распределяет сборки во времени, чтобы не допустить длительных остановок приложения, особенно при больших размерах кучи. При этом он лишен и некоторых других недостатков CMS, например, он не фрагментирует память.
Расплатой за достоинства G1 являются ресурсы процессора, которые он использует для выполнения достаточно большой части своей работы параллельно с основной программой. В результате страдает пропускная способность приложения. Целевым значением пропускной способности по умолчанию для G1 является 90%. Для Parallel GC, например, это значение равно 99%
Epsilon GC (Java 11)¶
Epsilon Garbage Collector на самом деле не собирает мусор. По сути он не делает ничего.
Предполагается использовать для замеров влияния сборщиков мусора на выполнение программы.
-XX:+UseEpsilonGC
Ссылки:
ZGC¶
ZGC нацелен обеспечить как можно более короткие этапы “stop-the-world”. Это достигается таким образом, что продолжительность этих пауз не увеличивается с размером кучи.
Эти характеристики делают ZGC подходящим для серверных приложений, где распространены большие кучи и требуется быстрое время отклика приложений.
To achieve its goals ZGC uses two techniques new to Hotspot Garbage Collectors: coloured pointers and load barriers.
Pointer colouring¶
Pointer colouring - это метод, который хранит информацию в самих указателях (ссылках).
Это возможно, потому что на 64-битных платформах (ZGC - только 64-битная) указатель может адресовать значительно больше памяти, чем реально может иметь система, и поэтому можно использовать некоторые другие биты для сохранения состояния.
ZGC ограничивает себя кучами 4 Тб, для которых требуется 42 бита, оставляя 22 бита возможного состояния, в котором в настоящее время используются 4 бита: финализируемый, переназначение, mark0 и mark1.
Одна проблема с Pointer colouring состоит в том, что он может создать дополнительную работу, когда вам нужно разыменовать указатель, потому что вам нужно замаскировать биты информации.
Some platforms like SPARC have built-in hardware support for pointer masking so it’s not an issue but for x86, the ZGC team use a neat multi-mapping trick.