Какие алгоритмы GC существуют?
4. Не используйте G1, если: 5. Не начинайте с тюнинга — сначала проверьте утечки 6. Мониторьте GC логи 7. Allocation Stall = увеличьте Heap или ConcGCThreads
🟢 Junior Level
Алгоритмы GC — разные способы “уборки мусора” в памяти.
Три основных подхода:
| Алгоритм | Как работает | Аналогия |
|---|---|---|
| Copying | Копирует живые в новую область | Переезд в новый дом |
| Mark-Sweep | Помечает живых, удаляет мёртвых | Выбросить мусор из комнаты |
| Mark-Compact | Помечает + сдвигает живых | Уборка + расстановка по местам |
Почему разные алгоритмы:
- Copying эффективен, когда мусора много (копируем только 2% живых).
- Mark-Sweep эффективен, когда мусора мало (не нужно копировать).
- Mark-Compact нужен для борьбы с фрагментацией.
Основные GC в Java:
- G1 GC — по умолчанию (Java 9+)
- ZGC (Java 15+) — паузы < 1 мс, не зависят от размера Heap. Throughput на 5-10% ниже G1.
- Parallel GC — для максимальной скорости обработки
🟡 Middle Level
GC Triangle
Latency (задержка)
/ \
/ \
Throughput ── Footprint
Нельзя оптимизировать все три!
Latency — насколько долго приложение «замирает» при GC (паузы).
Throughput — какой процент времени приложение работает, а не собирает мусор.
Footprint — сколько памяти потребляет JVM.
Современные GC
| GC | Пауза | Throughput | Для чего |
|---|---|---|---|
| Serial | 100-500 мс | Низкий | Маленькие приложения |
| Parallel | 100-1000 мс | Максимальный | Big Data, batch |
| G1 | 20-200 мс | Хороший | Web, Spring Boot |
| ZGC | < 1 мс | -5-10% | Real-time, HFT |
| Shenandoah | < 1 мс | -5-10% | Альтернатива ZGC |
Allocation Stall
Приложение создаёт мусор быстрее, чем GC собирает
→ Поток останавливается и ждёт GC
→ Пауза: сотни миллисекунд
Решение: больше Heap или меньше аллокаций
Full GC
Все GC имеют "план Б" — Full GC
→ Если конкурентная сборка не справляется
→ Полный STW всех поколений
→ Признак утечки или неправильной настройки
🔴 Senior Level
Write vs Load Barriers
G1: Write Barriers
→ При записи ссылки: пометить Card Table
→ overhead при записи
ZGC: Load Barriers
→ При чтении ссылки: проверить цвет указателя
→ Self-healing: исправить адрес на лету
→ overhead при чтении
SATB vs Incremental Update
SATB (Snapshot-At-The-Beginning):
→ Помечает всё, что было живо на начало разметки
→ Floating Garbage (объекты, ставшие недостижимыми после snapshot,
будут собраны в следующем цикле)
→ Используется в G1
Incremental Update:
→ Отслеживает новые ссылки
→ Меньше Floating Garbage
→ Больше overhead
Generational ZGC (Java 21+)
Разделение на Young/Old поколения
→ -50% CPU overhead
→ Young GC чаще и быстрее
→ Практически нет Allocation Stalls
Epsilon GC
«No-op GC» — только аллокация
→ Вообще не собирает мусор
→ При OOM → JVM падает
Epsilon GC — «пустой» сборщик: вообще не собирает мусор.
Выбрасывает OOM как только Heap заполнится.
Полезен для benchmark-ов и тестов с гарантированным временем жизни.
Зачем:
- Бенчмарки (исключить влияние GC)
- Короткоживущие функции
- Off-heap работа
Best Practices
- G1 GC — по умолчанию для большинства
- ZGC (Java 21+) — для SLA < 10 мс
- Parallel — для Big Data
- Не используйте G1, если:
- SLA < 10 мс — берите ZGC
- Batch processing без latency-требований — берите Parallel GC
- Не начинайте с тюнинга — сначала проверьте утечки
- Мониторьте GC логи
- Allocation Stall = увеличьте Heap или ConcGCThreads
Резюме для Senior
- GC Triangle: Latency ↔ Throughput ↔ Footprint
- Barriers: Write (G1) vs Load (ZGC)
- SATB = Floating Garbage trade-off
- Generational ZGC — лучший low-latency GC
- Allocation Stall = катастрофа для latency
- Epsilon — для тестов, не production
- Лучший мусор = не созданный
🎯 Шпаргалка для интервью
Обязательно знать:
- GC Triangle: нельзя оптимизировать Latency, Throughput и Footprint одновременно
- Copying — копирует живые (Young Gen, эффективен при 98% мусора); Mark-Sweep — помечает + удаляет (Old Gen); Mark-Compact — помечает + сдвигает (без фрагментации)
- Serial GC: 1 поток, долгие паузы — НЕ для серверов; Parallel GC: макс throughput, длинные паузы — для Big Data
- G1 GC: регионы 1-32 МБ, Mixed GC, паузы 20-200 мс — баланс; ZGC: Colored Pointers + Load Barriers, < 1 мс, overhead 5-15%
- Allocation Stall: приложение создаёт мусор быстрее, чем concurrent GC → поток замирает
- SATB (G1): Floating Garbage (объекты, ставшие недостижимыми после snapshot, будут собраны в следующем цикле)
- Epsilon GC: «no-op», не собирает мусор — только для бенчмарков
Частые уточняющие вопросы:
- Почему ZGC использует Load Barriers, а G1 — Write Barriers? — ZGC проверяет указатель при чтении (self-healing); G1 помечает Card Table при записи. Load Barriers дороже (~5-15 ns vs ~1-2 ns)
- Что такое Floating Garbage? — Объекты, умершие ПОСЛЕ начала разметки; не будут собраны в этом цикле (SATB trade-off)
- Когда Full GC — признак проблемы? — Когда случается регулярно (каждые минуты); признак утечки или неправильной настройки
- Почему Generational ZGC (Java 21+) лучше single-generational? — Разделение на Young/Old → -50% CPU overhead, Young GC чаще и быстрее
Красные флаги (НЕ говорить):
- «ZGC всегда лучше G1» — ZGC жертвует throughput на 5-15%; для batch processing G1/Parallel лучше
- «Я начну с тюнинга GC параметров» — сначала проверьте утечки и код; тюнинг GC не исправит bad code
- «Serial GC подходит для production сервера» — 1 поток, долгие паузы; только для маленьких CLI-утилит
Связанные темы:
- [[4. Что такое Garbage Collection]]
- [[13. Что такое G1 GC]]
- [[14. Что такое ZGC]]
- [[15. Что такое Shenandoah GC]]
- [[17. Какие GC минимизируют stop-the-world паузы]]