digital-garden/knowledge/dev/java/gc/Garbage First.md

13 KiB
Raw Blame History

aliases tags date zero-link parents linked
G1
зрелость/🌱
2023-11-06
../../../../garden/ru/meta/zero/00 Java разработка
../../../../garden/ru/dev/java/gc/Garbage Collector

Можно сказать, что этот сборщик пришел на замену CMS в Java 9. Также, как и остальные сборщики использует подход Generational Collection, однако Heap разделяется не на большие части Young, Old, а на небольшие равные регионы.

Размер регионов по умолчанию 1 mb, но можно указать произвольное значение до 32 mb. Общее количество регионов в куче равно размеру кучи, деленному на размер региона. При настройке G1 рекомендуется ориентироваться на 2048 регионов, чтобы G1 работал эффективно.

И уже каждый сегмент может быть помечен как:

  • eden - новые объекты, тут происходит аллокация.
  • survivor - объекты пережившие несколько сборок
  • old - долгоживущие объекты

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

[!INFO] Большой объект (humongous) Если объект занимает 2 и более сегмента, то он не перемещается за исключением Full GC. И в тот же сегмент не добавляются другие объекты, даже если у вас хватает места в регионе.

В Java 11 были внесены изменения в обработку больших объектов.

Minor Collection

Minor сборка обычно происходит, когда eden заполняется. Над minor сборками мусора работают несколько потоков. Но очистка выполняется не на всем поколении, а только на регионах молодого поколения. За счет этого количество StopTheWorld становится больше, но сборка выполняется быстрее и не превышает желаемое время.

Mixed Collection

Mixed сборка обрабатывает как молодое поколение, так и часть старого поколения. Эта запускается после определенного количества minor сборок.

В G1 существует процесс, называемый Marking Cycle, который работает параллельно с основным приложением и составляет список живых объектов. Он напоминает работу сборщика CMS.

  • Initial mark. Пометка GC Root с использованием информации от minor сборок.
    • В Java 10 были внедрены улучшения. Параллельная пометка и улучшенное использование ресурсов процессора.
  • Concurrent marking. Пометка всех живых объектов в heap. Работает в нескольких потоках.
  • Remark. Дополнительный поиск не учтенных ранее живых объектов. Используется StopTheWorld
  • Cleanup. Очистка вспомогательных структур учета ссылок на объект и поиск пустых регионов, которые уже можно использовать для размещения новых объектов. Первая часть этого шага выполняется с использованием StopTheWorld.

Следует иметь в виду, что для получения списка живых объектов G1 использует алгоритм Snapshot at the beginning (SATB), то есть в список живых попадают все объекты, которые были таковыми на момент начала работы алгоритма, плюс все объекты, созданные за время его выполнения. Это, в частности, означает, что G1 допускает наличие плавающего мусора.

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

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

Full GC

Может оказаться так, что в процессе очистки памяти в куче не остается свободных регионов, в которые можно было бы копировать выжившие объекты. Это приводит к возникновению ситуации allocation (evacuation) failure. В таком случае сборщик выполняет полную сборку мусора по всей куче при остановленных основных потоках приложения.

С Java 9 была реализована параллельная сборка мусора в этом режиме.

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

Дефрагментация

G1 копирует объекты из одной или нескольких областей кучи в одну область кучи, при этом одновременно сжимая и освобождая память. То есть используется подход Copy Collector. Этот процесс выполняется параллельно, чтобы уменьшить время пауз и увеличить пропускную способность.

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

Remembered Set

  • Хранит информацию о местонахождении ссылок на объекты из региона
    • Из старого в молодое поколение
    • Между регионами в старом поколении
  • Позволяет собирать регионы независимо

Ситуации StopTheWorld

Если резюмировать, то у G1 мы получаем STW в следующих случаях:

  1. Процессы переноса объектов между поколениями. Для минимизации таких пауз G1 использует несколько потоков, то есть Parallel Collection
  2. Короткая фаза начальной пометки корней в рамках цикла пометки.
  3. Более длинная пауза в конце фазы remark и в начале фазы cleanup цикла пометки.

Работа с барьерами

Для работы G1 нужны барьеры 2 типов. Используются они для поддержания согласованности heap в момент сборки мусора.

  • Pre-barrier
    • Работает только, когда идет фоновая сборка мусора.
    • Сохраняет старое значение поля
    • Поддерживается корректность фоновой маркировки
    • SATB
  • Post-barrier
    • Поддержка актуальности Remembered Set
    • Не срабатывают, когда мы модифицируем указатель из молодого объекта в молодой

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

Выводы

Минусы:

  • Очень плохо работает с объектами, которые занимают в памяти больше 1 mb.
  • Не является сборщиком мусора в реальном времени. Имеются StopTheWorld.
  • Потребляет много ресурсов CPU, до 10% от всего потребления приложением.

Плюсы:

  • Фоновый и параллельный.
  • Ожидаемое, но не гарантированное, время пауз StopTheWorld.
  • Слабо подвержен фрагментации.

Когда использовать:

  • Нужна хорошая производительность
  • Изначально позиционировался для приложений, размер heap которых больше 4-5 Gb
  • Продолжительность пауз 0,5-1 s
  • Минимальная настройка
  • Занятость кучи больше 50%
  • Скорость создания объектов серьезно варьируется

Когда не использовать:

  • Жесткие требования на паузы меньше 100 мс
  • Требуется максимально возможный throughput даже в ущерб latency

Как включить:

  • -XX:+UseG1GC - включение сборщика
  • -XX:MaxGCPauseMillis= максимальная пауза для сборки мусора
  • -XX:GCPauseIntervalMills= интервал между паузами

Дополнительный материал