Почему не стоит вызывать System.gc()?
4. Не ловите OOME через System.gc() 5. Ищите утечку, а не "помогайте" GC
🟢 Junior Level
System.gc() — плохая практика, потому что:
- Останавливает приложение — все потоки ждут
- GC умнее вас — сам знает, когда убирать
- Тратит 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
- Единственный легитимный кейс — бенчмарки (JMH)
- JFR для поиска вызывающего
- ExplicitGCInvokedConcurrent для production
- Не ловите OOME через System.gc()
- Ищите утечку, а не “помогайте” 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]]