Вопрос 12 · Раздел 3

Какие алгоритмы GC существуют?

4. Не используйте G1, если: 5. Не начинайте с тюнинга — сначала проверьте утечки 6. Мониторьте GC логи 7. Allocation Stall = увеличьте Heap или ConcGCThreads

Версии по языкам: English Russian Ukrainian

🟢 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

  1. G1 GC — по умолчанию для большинства
  2. ZGC (Java 21+) — для SLA < 10 мс
  3. Parallel — для Big Data
  4. Не используйте G1, если:
    • SLA < 10 мс — берите ZGC
    • Batch processing без latency-требований — берите Parallel GC
  5. Не начинайте с тюнинга — сначала проверьте утечки
  6. Мониторьте GC логи
  7. 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 паузы]]