Как работает G1 Garbage Collector
G1 (Garbage-First) — это сборщик мусора, ставший используемым по умолчанию с Java 9 (JEP 248). Его основная цель — обеспечить предсказуемые паузы при сохранении высокой пропускной способности.
Аналогия из жизни: представьте город, разделённый на районы. Вместо того чтобы убирать весь город сразу (что парализует движение), коммунальная служба выбирает самые замусоренные районы и убирает их в первую очередь. Отсюда название Garbage-First — мусор в первую очередь.
Регионы (Regions)
G1 делит всю кучу на множество регионов одинакового размера (обычно 1-32 MB, автоматически подбирается так, чтобы было около 2048 регионов). Каждый регион может быть:
| Тип региона | Назначение |
|---|---|
| Eden | Новые объекты |
| Survivor | Объекты, пережившие Minor GC |
| Old | Долгоживущие объекты |
| Humongous | Объекты размером > 50% региона |
Пример
+---+---+---+---+---+---+---+---+---+---+
| E | E | S | | O | O | H | H | | E |
+---+---+---+---+---+---+---+---+---+---+
E = Eden, S = Survivor, O = Old, H = Humongous, пусто = свободный
Фазы работы G1
-
Young-only Collection (Minor GC) — собирает только Eden и Survivor регионы. Живые объекты копируются в Survivor или Old. Stop-the-world, но обычно очень быстрая (< 10 мс)
-
Concurrent Marking (конкурентная разметка):
- Initial Mark (STW) — отмечает корневые объекты, совмещается с Young GC
- Concurrent Mark — обход графа объектов параллельно с приложением
- Remark (STW) — завершение разметки, обработка изменений (SATB)
- Cleanup (частично STW) — подсчёт живых объектов, выбор регионов для сборки
-
Mixed Collection (смешанная сборка) — собирает и Young, и выбранные Old регионы с наибольшим количеством мусора
Ключевые параметры G1
# Целевое время паузы (по умолчанию 200 мс)
-XX:MaxGCPauseMillis=100
# Размер региона (1-32 MB, степень двойки)
-XX:G1HeapRegionSize=16m
# Порог заполнения heap для запуска concurrent marking (по умолчанию 45%)
-XX:InitiatingHeapOccupancyPercent=45
# Включить дедупликацию строк
-XX:+UseStringDeduplication
# Количество потоков для параллельных фаз
-XX:ParallelGCThreads=8
# Количество потоков для конкурентных фаз
-XX:ConcGCThreads=4
Humongous-объекты
Объекты, занимающие более 50% размера региона, считаются humongous. Они размещаются в специальных humongous-регионах и собираются только при concurrent marking или Full GC. Большое количество humongous-объектов негативно влияет на производительность.
String Deduplication
G1 поддерживает дедупликацию строк (-XX:+UseStringDeduplication). Если несколько объектов String содержат одинаковый массив символов, G1 заставляет их использовать один и тот же byte[], экономя память.
Частые ошибки
- Установка слишком низкого
MaxGCPauseMillis(< 20 мс) — G1 будет слишком часто собирать мусор - Ручное задание размеров Young Generation (
-Xmn) при использовании G1 — отключает адаптивное управление - Игнорирование humongous-аллокаций — они обходят стандартный механизм и могут вызывать внеочередные GC
- Отсутствие GC-логов:
-Xlog:gc*:file=gc.log— без них невозможно настроить G1
На собеседовании: объясните три ключевые идеи G1: деление кучи на регионы (вместо фиксированного Young/Old), приоритет сборки самых замусоренных регионов, и целевое время паузы (
MaxGCPauseMillis). Упомяните, что это цель, а не гарантия. Если спрашивают о Full GC — до Java 10 он был однопоточным, с Java 10+ стал многопоточным.