Gymterview
middle

Как работает 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

  1. Young-only Collection (Minor GC) — собирает только Eden и Survivor регионы. Живые объекты копируются в Survivor или Old. Stop-the-world, но обычно очень быстрая (< 10 мс)

  2. Concurrent Marking (конкурентная разметка):

    • Initial Mark (STW) — отмечает корневые объекты, совмещается с Young GC
    • Concurrent Mark — обход графа объектов параллельно с приложением
    • Remark (STW) — завершение разметки, обработка изменений (SATB)
    • Cleanup (частично STW) — подсчёт живых объектов, выбор регионов для сборки
  3. 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+ стал многопоточным.