Добавить в корзинуПозвонить
Найти в Дзене
Записки о Java

Garbage-First (G1) GC в Java 11: баланс между пропускной способностью и предсказуемыми паузами

Стек: Java 11, JVM, Garbage Collection
Цель: понять, как работает G1 GC, почему он стал GC по умолчанию, и как его настроить под ваше приложение. G1 (Garbage-First) — это серверный сборщик мусора, разработанный для: 💡 Основная идея:
Разделить кучу на регионы и собирать мусор “там, где его больше всего” — отсюда и название Garbage-First. Начиная с Java 9, G1 GC является сборщиком по умолчанию на server-class машинах. В отличие от Serial/Parallel/CMS, G1 не делит кучу на Young/Old как непрерывные области.
Вместо этого: 📌 Преимущество:
G1 может собирать мусор инкрементально, не останавливая всё приложение надолго. Фаза Тип Описание Initial Mark STW Помечает корни (обычно совмещается с Minor GC) Root Region Scanning Concurrent Сканирует ссылки из Survivor-регионов в Old Concurrent Marking Concurrent Помечает все живые объекты во всей куче Remark STW Завершает пометку, обрабатывает изменения за время concurrent marking Cleanup STW + Concurrent Определяет регионы с наибольшим количес
Оглавление

Стек: Java 11, JVM, Garbage Collection
Цель: понять, как работает G1 GC, почему он стал GC по умолчанию, и как его настроить под ваше приложение.

🔍 Что такое G1 GC?

G1 (Garbage-First) — это серверный сборщик мусора, разработанный для:

  • Больших heaps (от 6 ГБ и выше),
  • Предсказуемых пауз (по умолчанию <200 мс),
  • Высокой пропускной способности.
💡 Основная идея:
Разделить кучу на регионы и собирать мусор “там, где его больше всего” — отсюда и название Garbage-First.

Начиная с Java 9, G1 GC является сборщиком по умолчанию на server-class машинах.

Архитектура G1: регионы вместо поколений

В отличие от Serial/Parallel/CMS, G1 не делит кучу на Young/Old как непрерывные области.
Вместо этого:

  • Вся куча делится на равные по размеру регионы (обычно 1–32 МБ, в зависимости от heap size).
  • Каждый регион может быть:Eden (младшее поколение),
    Survivor (младшее поколение),
    Old (старшее поколение),
    Humongous (для объектов >50% региона).
📌 Преимущество:
G1 может
собирать мусор инкрементально, не останавливая всё приложение надолго.

🔄 Как работает G1 GC?

Фазы цикла G1:

Фаза

Тип

Описание

Initial Mark

STW

Помечает корни (обычно совмещается с Minor GC)

Root Region Scanning

Concurrent

Сканирует ссылки из Survivor-регионов в Old

Concurrent Marking

Concurrent

Помечает все живые объекты во всей куче

Remark

STW

Завершает пометку, обрабатывает изменения за время concurrent marking

Cleanup

STW + Concurrent

Определяет регионы с наибольшим количеством мусора и освобождает их

Ключевые особенности:

  • Mixed GC: после фазы Cleanup G1 начинает смешанные циклы, собирая и Young, и часть Old-регионов.
  • Компактификация “на лету”: при копировании объектов из одного региона в другой — фрагментация устраняется автоматически.
  • Паузы STW короткие и предсказуемые.
⚠️ Важно: G1 гарантирует целевые паузы, но жертвует пропускной способностью по сравнению с Parallel GC.

Как включить G1 GC?

В Java 11 G1 включён по умолчанию на серверах.
Явное включение (для уверенности):

java -XX:+UseG1GC -jar myapp.jar

Проверить текущий GC:

jcmd <pid> VM.flags | grep UseG1GC

Преимущества G1 GC

Плюс

Объяснение

Предсказуемые паузы

Целевые паузы задаются через -XX:MaxGCPauseMillis

Отсутствие фрагментации

Компактификация происходит при каждом копировании

Эффективен на больших heaps

Работает отлично на heaps 6–100+ ГБ

Единый GC для Young и Old

Нет резких переходов к Full GC (в отличие от CMS)

💡 Идеален для:Веб-приложений (Spring Boot, Tomcat),
Микросервисов,
Систем с требованиями к latency (<500 мс).

❌ Недостатки G1 GC

Минус

Объяснение

Сложность

Много внутренних параметров, трудно отлаживать

Overhead памяти

~5–10% heap уходит на служебные структуры

Меньшая throughput, чем Parallel GC

Не лучший выбор для batch-обработки

Чувствителен к allocation rate

При очень высокой скорости выделения памяти — может не успевать

Настройка G1 GC в Java 11

Хотя G1 часто работает «из коробки», полезные параметры:

# Установить размер heap (обязательно!)

-Xms8g -Xmx8g

# Целевая максимальная пауза (по умолчанию 200 мс)

-XX:MaxGCPauseMillis=100

# Процент heap, который можно использовать для humongous objects

-XX:G1HeapRegionSize=32m # обычно не нужно задавать

# Количество потоков GC (по умолчанию ~1/4 CPU cores)

-XX:ParallelGCThreads=8

-XX:ConcGCThreads=2

# Логирование GC

-Xlog:gc*:file=g1-gc.log:time,uptime,pid,tid,level,tags

💡 Главный совет:
Сначала задайте -Xmx, затем настройте MaxGCPauseMillis, и только потом — остальные параметры.

Пример лога G1 GC

[2024-06-01T12:00:00.000+0300][1000ms] GC(0) Pause Young (Normal) (G1 Evacuation Pause)

[2024-06-01T12:00:00.050+0300][1050ms] GC(0) 4000M->2000M(8192M) 50.234ms

[2024-06-01T12:05:00.000+0300][300000ms] GC(15) Pause Remark

[2024-06-01T12:05:00.100+0300][300100ms] GC(15) 8192M->7000M(8192M) 100.123ms

[2024-06-01T12:05:01.000+0300][301000ms] GC(16) Pause Cleanup

[2024-06-01T12:05:01.010+0300][301010ms] GC(16) To-space exhausted, 8192M->7500M(8192M) 10.456ms

Ищите:

  • Pause Young — Minor GC,
  • Pause Remark / Pause Cleanup — часть concurrent cycle,
  • To-space exhausted — предупреждение: G1 не успевает освобождать память.

Типичные проблемы и решения

Проблема

Причина

Решение

Длинные паузы

MaxGCPauseMillis слишком мал

Увеличьте до 200–300 мс

To-space exhausted

Слишком высокая allocation rate

Увеличьте heap или добавьте CPU

Частые Mixed GC

Много мусора в Old Gen

Оптимизируйте код (уменьшите утечки)

Высокий overhead

Много humongous objects

Избегайте массивов >50% региона

Заключение

G1 GC — это золотая середина современной Java:

  • Он не самый быстрый (уступает Parallel в throughput),
  • Но не самый медленный в паузах (лучше CMS и Parallel),
  • И надёжно работает “из коробки” на большинстве серверов.
🔑 Используйте G1, если:У вас heap >6 ГБ,
Вы хотите
предсказуемые паузы без ручной настройки,
Ваше приложение —
веб-сервис или микросервис.

В Java 11 G1 — разумный выбор по умолчанию, и вам, скорее всего, не придётся его менять.