Питання 28 · Розділ 3

Чому не варто викликати System.gc()?

4. Не ловіть OOME через System.gc() 5. Шукайте витік, а не «допомагайте» GC

Мовні версії: English Russian Ukrainian

🟢 Junior Level

System.gc() — погана практика, тому що:

  1. Зупиняє додаток — всі потоки чекають
  2. GC розумніший за вас — сам знає, коли прибрати
  3. Тратить CPU — часто без користі
// ❌ Не робіть так!
System.gc();  // «Допомагаю» GC

// ✅ Довіртеся JVM
// GC сам вирішить, коли потрібно

🟡 Middle Level

Руйнування адаптивних моделей

Сучасні GC (G1, ZGC) само-навчаються:
  → Allocation Rate
  → Promotion Rate
  → Pause Time Goals

System.gc() = зовнішній шок:
  → Скидає статистику
  → GC вчиться заново
  → Непередбачувані паузи

Concurrent Mode Failure

Concurrent Mode Failure (G1) — concurrent marking не встиг завершити,
Old Gen заповнився → fallback до Full GC. Це найдовша пауза в G1.
System.gc() може спровокувати цей сценарій, якщо викликаний у непідходящий момент.

Як знайти винуватця

# 1. GC Logs
-Xlog:gc*
# → "Pause Full (System.gc())"

# 2. JFR
# → Подія "System GC"
# → Повний стек-трейс

# 3. Рішення
-XX:+ExplicitGCInvokedConcurrent  # Конкурентно
# або
-XX:+DisableExplicitGC  # Ігнорувати

🔴 Senior Level

Деоптимізація JIT

Full GC → class unloading → деоптимізація гарячих методів.
Code Cache очищується окремо (CodeCache sweeper).

  → Після GC: інтерпретація коду
  → Throughput падає на секунди

→ «Кувалда для швейцарського годинника» — System.gc() грубо зупиняє
  всі оптимізації JVM ради примусового збирання сміття.

GC Thrashing

Спроба «полікувати» OOM через System.gc():
  → Звільняє крихти пам'яті
  → Тратить 95% CPU на GC
  → Маскує реальну проблему

→ Краще нехай впаде і перезапуститься

Production: DisableExplicitGC

-XX:+DisableExplicitGC
# → Ігнорує всі виклики System.gc()
# → Крім Direct Buffers (обережно!)

# Альтернатива:
-XX:+ExplicitGCInvokedConcurrent
# → Конкурентний GC замість Full GC STW

Best Practices

  1. Єдиний легітимний кейс — бенчмарки (JMH)
  2. JFR для пошуку того, хто викликає
  3. ExplicitGCInvokedConcurrent для production
  4. Не ловіть OOME через System.gc()
  5. Шукайте витік, а не «допомагайте» GC

Резюме для Senior

  • System.gc() = «кувалда для швейцарського годинника» — грубо зупиняє всі оптимізації JVM
  • Руйнує адаптивні моделі GC
  • Concurrent Mode Failure → Full GC STW
  • JIT деоптимізація → втрата Throughput
  • JFR = знайти того, хто викликає
  • DisableExplicitGC або ExplicitGCInvokedConcurrent

🎯 Шпаргалка для інтерв’ю

Обов’язково знати:

  • System.gc() — антипатерн: Full GC = STW (секунди), скидає адаптивну статистику GC, JIT деоптимізація гарячих методів
  • G1/ZGC само-навчаються (Allocation Rate, Promotion Rate, Pause Time Goals); System.gc() = зовнішній шок → GC вчиться заново
  • Concurrent Mode Failure (G1): concurrent marking не встиг → Old Gen заповнений → Full GC STW; System.gc() може спровокувати
  • JIT деоптимізація: Full GC → class unloading → гарячі методи → інтерпретація → throughput падає на секунди
  • GC Thrashing: спроба «полікувати» OOM через System.gc() → 95% CPU на GC → маскує реальну проблему
  • Production: -XX:+ExplicitGCInvokedConcurrent (конкурентний GC замість STW) або -XX:+DisableExplicitGC (ігнорувати, обережно з Direct Buffers!)
  • Єдиний легітимний кейс — бенчмарки (JMH)

Часті уточнюючі запитання:

  • Чому System.gc() руйнує адаптивні моделі? — G1/ZGC накопичують статистику (allocation rate, promotion rate); System.gc() = раптовий Full GC → статистика скинута → GC «забуває» поведінку додатку
  • Що таке Concurrent Mode Failure? — G1 concurrent marking не встиг завершити до заповнення Old Gen → fallback до Full GC (найдовша пауза в G1)
  • Як знайти, хто викликає System.gc()?-Xlog:gc* (шукайте “Pause Full (System.gc())”); JFR (подія “System GC” з повним стек-трейсом)
  • Чому JIT деоптимізація відбувається? — Full GC → class unloading → JIT-скомпільовані методи видалені → перекомпіляція заново

Червоні прапорці (НЕ говорити):

  • «Викликаю System.gc() для профілактики» — GC сам знає; ручний виклик = STW + руйнування адаптивних моделей
  • «Ловлю OOME і викликаю System.gc()» — GC Thrashing: 95% CPU на GC; краще fail-fast і перезапуск
  • «System.gc() покращує продуктивність» — «кувалда для швейцарського годинника»; грубо зупиняє всі оптимізації JVM

Пов’язані теми:

  • [[27. Чи можна вручну викликати GC]]
  • [[4. Що таке Garbage Collection]]
  • [[13. Що таке G1 GC]]
  • [[19. Що станеться при OutOfMemoryError]]
  • [[16. Що таке stop-the-world]]