Question 28 · Section 3

Why you should not call System.gc()?

4. Don't catch OOME via System.gc() 5. Find the leak, don't "help" GC

Language versions: English Russian Ukrainian

Junior Level

System.gc() — bad practice because:

  1. Stops the application — all threads wait
  2. GC is smarter than you — knows when to clean itself
  3. Wastes CPU — often without benefit
// ❌ Don't do this!
System.gc();  // "Helping" GC

// ✅ Trust the JVM
// GC will decide when it's needed

Middle Level

Destruction of Adaptive Models

Modern GCs (G1, ZGC) are self-learning:
  → Allocation Rate
  → Promotion Rate
  → Pause Time Goals

System.gc() = external shock:
  → Resets statistics
  → GC relearns from scratch
  → Unpredictable pauses

Concurrent Mode Failure

Concurrent Mode Failure (G1) — concurrent marking didn't finish in time,
Old Gen filled up → fallback to Full GC. This is the longest pause in G1.
System.gc() can provoke this scenario if called at the wrong moment.

How to Find the Culprit

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

# 2. JFR"System GC" event
→ Full stack trace

# 3. Solution
-XX:+ExplicitGCInvokedConcurrent  # Concurrent
# or
-XX:+DisableExplicitGC  # Ignore

Senior Level

JIT Deoptimization

Full GC → class unloading → deoptimization of hot methods.
Code Cache is cleaned separately (CodeCache sweeper).

  → After GC: code interpretation
  → Throughput drops for seconds

→ "Sledgehammer for Swiss watches" — System.gc() brutally stops
  all JVM optimizations for forced garbage collection.

GC Thrashing

Attempting to "cure" OOM via System.gc():
  → Frees tiny amounts of memory
  → Spends 95% CPU on GC
  → Masks the real problem

→ Better to crash and restart

Production: DisableExplicitGC

-XX:+DisableExplicitGC
# → Ignores all System.gc() calls
# → Except Direct Buffers (be careful!)

# Alternative:
-XX:+ExplicitGCInvokedConcurrent
# → Concurrent GC instead of Full GC STW

Best Practices

  1. Only legitimate use case — benchmarks (JMH)
  2. JFR to find the caller
  3. ExplicitGCInvokedConcurrent for production
  4. Don’t catch OOME via System.gc()
  5. Find the leak, don’t “help” GC

Senior Summary

  • System.gc() = “sledgehammer for Swiss watches” — brutally stops all JVM optimizations
  • Destroys adaptive GC models
  • Concurrent Mode Failure → Full GC STW
  • JIT deoptimization → Throughput loss
  • JFR = find the caller
  • DisableExplicitGC or ExplicitGCInvokedConcurrent

Interview Cheat Sheet

Must know:

  • System.gc() — anti-pattern: Full GC = STW (seconds), resets GC adaptive statistics, JIT deoptimization of hot methods
  • G1/ZGC are self-learning (Allocation Rate, Promotion Rate, Pause Time Goals); System.gc() = external shock → GC relearns from scratch
  • Concurrent Mode Failure (G1): concurrent marking didn’t finish → Old Gen full → Full GC STW; System.gc() can provoke this
  • JIT deoptimization: Full GC → class unloading → hot methods → interpretation → throughput drops for seconds
  • GC Thrashing: attempting to “cure” OOM via System.gc() → 95% CPU on GC → masks real problem
  • Production: -XX:+ExplicitGCInvokedConcurrent (concurrent GC instead of STW) or -XX:+DisableExplicitGC (ignore, careful with Direct Buffers!)
  • Only legitimate use case — benchmarks (JMH)

Common follow-up questions:

  • Why does System.gc() destroy adaptive models? — G1/ZGC accumulate statistics (allocation rate, promotion rate); System.gc() = sudden Full GC → statistics reset → GC “forgets” application behavior
  • What is Concurrent Mode Failure? — G1 concurrent marking didn’t finish before Old Gen filled → fallback to Full GC (longest pause in G1)
  • How to find who calls System.gc()?-Xlog:gc* (look for “Pause Full (System.gc())”); JFR (“System GC” event with full stack trace)
  • Why does JIT deoptimization happen? — Full GC → class unloading → JIT-compiled methods removed → recompiled from scratch

Red flags (DO NOT say):

  • “I call System.gc() for preventive maintenance” — GC knows itself; manual call = STW + destruction of adaptive models
  • “I catch OOME and call System.gc()” — GC Thrashing: 95% CPU on GC; better fail-fast and restart
  • “System.gc() improves performance” — “sledgehammer for Swiss watches”; brutally stops all JVM optimizations

Related topics:

  • [[27. Can you manually invoke GC]]
  • [[4. What is Garbage Collection]]
  • [[13. What is G1 GC]]
  • [[19. What happens on OutOfMemoryError]]
  • [[16. What is stop-the-world]]