Выбирал ли ты GC хоть раз осознанно? Часто запустил приложение — работает, и ладно. Но когда начинаются проблемы с латентностью или throughput, первый вопрос от SRE: «А какой у вас GC?».
🔹 Разберём что выбрать и почему.
Все GC балансируют между двумя вещами:
▪️ Throughput — сколько полезной работы делает приложение за единицу времени
▪️ Latency — насколько долго приложение паузится на GC
Идеального нет. Выбор всегда зависит от профиля нагрузки.
🔹 Serial и Parallel — если латентность не важна
-XX:+UseSerialGC / -XX:+UseParallelGC
Serial GC однопоточный. И Minor, и Major GC выполняет один поток, всё приложение стоит. Звучит как антиквариат, но на маленьком heap (< 256MB) и однопоточной среде накладные расходы на координацию потоков не нужны, Serial реально быстрее.
Parallel GC — то же самое, но сборку делают несколько потоков одновременно. STW никуда не девается, просто сама пауза короче за счёт параллелизма. На Major GC всё равно можешь получить сотни миллисекунд.
Где уместно: batch-обработка, утилиты, CLI-инструменты. Там где важен максимальный throughput и никто не ждёт response time.
🔹 G1 — дефолт с Java 9
-XX:+UseG1GC
G1 делит heap на регионы фиксированного размера (~1-32MB) и собирает их инкрементально. Не нужно собирать весь heap за раз — выбираются регионы с наибольшим количеством мусора (отсюда "Garbage First").
Главная настройка:
bash-XX:MaxGCPauseMillis=200 # целевое время паузы
Это не гарантия, это цель. G1 будет подстраивать размер собираемых регионов, чтобы уложиться.
Где уместно: большинство серверных приложений с heap от 4GB. Хороший баланс throughput/latency без тюнинга.
🔹 ZGC и Shenandoah — когда паузы недопустимы
-XX:+UseZGC / -XX:+UseShenandoahGC
Оба делают большую часть работы конкурентно — параллельно с работой приложения. STW-паузы измеряются единицами миллисекунд вне зависимости от размера heap.
Где уместно: real-time системы, trading, API с жёсткими SLA по p99 латентности. Платишь немного throughput — получаешь предсказуемые паузы.
🔹 Как выбирать на практике
Не угадывать, а мерить. Включаешь GC логи и смотришь что происходит:
bash-Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=20m
Дальше либо GCEasy, либо руками смотришь на паузы и частоту сборок.
🔹 Три вопроса которые определяют выбор
1. Какой размер heap?
До 4GB — G1 справится. Больше 16GB — смотри на ZGC, у G1 начинаются долгие Mixed GC.
2. Какие требования к латентности?
Есть SLA на p99 < 50ms — только ZGC или Shenandoah. Нет жёстких требований — G1.
3. Какой профиль аллокаций?
Много short-lived объектов — генерационный GC справится хорошо. Много long-lived крупных объектов — G1 может давать длинные паузы на evacuation, ZGC справится лучше.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#CoreJava