Вопрос 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]]