Garbage Collection

Java Garbage Collection — how GC works, G1GC vs ZGC, tuning for production, and diagnosing GC-related performance issues.

Last reviewed: January 2025

·Java 8Java 11Java 17Java 21
✅
Quick Understanding: Garbage Collection automatically frees memory occupied by objects that are no longer reachable. You create objects with new; the GC cleans them up when nothing references them. The GC runs in background threads and occasionally pauses your application (Stop-The-World) to safely collect garbage. Modern GCs (G1GC, ZGC) minimize these pauses.

🔍 What Is It?

Generational Hypothesis:

Most objects die young. GC is optimized for this:

  • →Young Generation (Eden + S0 + S1): new objects allocated here; Minor GC cleans it frequently and fast
  • →Old Generation (Tenured): objects that survived multiple Minor GCs; Major GC cleans it rarely but slowly

GC Algorithms:

Serial GC (-XX:+UseSerialGC)

  • →Single-threaded; Stop-The-World for all collections
  • →For single-CPU machines and small heaps only

Parallel GC (-XX:+UseParallelGC)

  • →Multi-threaded; still Stop-The-World
  • →Default in Java 8; good throughput, higher pauses

G1GC (-XX:+UseG1GC)

  • →Default in Java 9+
  • →Divides heap into equal-sized regions
  • →Concurrent marking with short STW pauses
  • →Targets pause time goal (-XX:MaxGCPauseMillis=200)
  • →Good for heaps 4GB–32GB

ZGC (-XX:+UseZGC)

  • →Java 15+ production-ready; sub-millisecond STW pauses
  • →Fully concurrent — almost no STW
  • →Works for heaps from 8MB to 16TB
  • →Best for latency-sensitive applications

Shenandoah (-XX:+UseShenandoahGC)

  • →Similar to ZGC; available in OpenJDK builds
  • →Sub-millisecond pauses; fully concurrent

🎯 Why Do We Need It?

Without GC, developers must manually free memory (like C/C++):

  • →malloc() to allocate; free() to release
  • →Forgetting free() = memory leak
  • →Double-free() = crash
  • →Use-after-free = security vulnerability

Java's GC eliminates these entire categories of bugs. The tradeoff: GC pauses and overhead. Modern GCs have reduced this tradeoff to near-zero for most applications.

🏗️ What Happens in Real Projects?

GC tuning is a real production concern:

  • →High GC frequency = too many short-lived objects (fix: reduce object allocation)
  • →Long GC pauses = old generation too full (fix: increase heap, tune GC)
  • →GC CPU overhead > 5% = GC working too hard (fix: heap tuning, reduce allocation)
  • →Memory leak = heap grows until OOM (fix: heap dump analysis)

⚙️ How It Works

Minor GC (Young Generation):

  1. Objects allocated in Eden
  2. When Eden is full, Minor GC runs
  3. Live objects copied to Survivor space (S0)
  4. Next Minor GC: live objects from Eden + S0 copied to S1
  5. After N collections (default: 15), promoted to Old Generation
  6. Eden and empty Survivor space cleared

Major/Full GC (Old Generation):

  1. Triggered when Old Generation fills up
  2. Mark all live objects (may be concurrent in G1/ZGC)
  3. Compact/collect garbage
  4. Stop-The-World pause (shorter in G1, near-zero in ZGC)

GC Roots (starting points for reachability analysis):

  • →Local variables on stack
  • →Static fields
  • →JNI references
  • →Active threads

💻 Example

GC Tuning and Monitoring — How to tune and monitor GC in production Java applications.

bash
# G1GC — recommended for most applications
java -Xms4g -Xmx4g      -XX:+UseG1GC      -XX:MaxGCPauseMillis=200     # target max pause (best effort)
     -XX:G1HeapRegionSize=16m     # region size (1m-32m, power of 2)
     -XX:InitiatingHeapOccupancyPercent=45  # start concurrent marking at 45%
     -Xlog:gc*:file=gc.log:time,uptime,level      -XX:+HeapDumpOnOutOfMemoryError      -XX:HeapDumpPath=/logs/heap.hprof      -jar app.jar

# ZGC — for latency-sensitive applications (Java 15+)
java -Xms8g -Xmx8g      -XX:+UseZGC      -XX:SoftMaxHeapSize=6g       # soft limit, ZGC tries to stay under this
     -Xlog:gc*:file=gc.log:time,uptime,level      -jar app.jar

# Container-aware settings (Docker/Kubernetes)
java -XX:+UseContainerSupport     # respect container memory limits (default Java 10+)
     -XX:MaxRAMPercentage=75.0    # use 75% of container memory for heap
     -XX:+UseG1GC      -jar app.jar

🤔 Common Confusions

❓ Does calling System.gc() force garbage collection?

System.gc() is a hint to the JVM to run GC — it does not guarantee it will happen, and the JVM may ignore it. Calling it explicitly is almost never the right solution. If you are having memory issues, fix the root cause (memory leak, heap too small) rather than trying to force GC.

❓ What is a Stop-The-World pause?

A Stop-The-World (STW) pause freezes all application threads so the GC can safely inspect the heap without objects moving around. During STW, your application is completely unresponsive. Modern GCs (G1, ZGC) minimize STW by doing most work concurrently while the application runs. ZGC achieves sub-millisecond STW even on terabyte heaps.

❓ Why does increasing heap size sometimes make GC worse?

A larger heap means more objects to scan during Full GC, which means longer pauses. With a 1GB heap, a Full GC might take 1 second. With a 32GB heap, it might take 30 seconds. G1GC and ZGC are designed for large heaps. For G1GC, keep heap under 32GB. For larger heaps, use ZGC.

🚨 Common Production Issues

GC overhead limit exceeded — GC spending more than 98% of time collecting less than 2% of heap. Usually a memory leak. Analyze with heap dump.

Promotion failure in G1GC — Old Generation fills up during Minor GC, triggering Full GC. Increase heap or tune -XX:InitiatingHeapOccupancyPercent.

Humongous allocations in G1GC — objects larger than 50% of region size go directly to Old Generation, bypassing Young. Tune -XX:G1HeapRegionSize or reduce large object allocations.

✅ Best Practices

Use G1GC for most applications (default Java 9+); ZGC for latency-sensitive or large heaps.

Set -Xms = -Xmx to prevent heap resizing pauses.

Enable GC logging in production: -Xlog:gc*:file=gc.log:time,uptime

Monitor GC pause time and frequency — alert when pauses exceed SLA.

Reduce object allocation in hot paths — the best GC is no GC.

Use -XX:+HeapDumpOnOutOfMemoryError for automatic heap dump on OOM.

🏛️ Architect's Note

Senior Architect's Perspective

GC tuning is the last resort, not the first. The best way to reduce GC pressure is to allocate fewer objects: use primitives instead of wrappers, reuse objects, use streaming instead of materializing large collections, and avoid unnecessary String concatenation in loops. Profile your application's allocation rate with JFR (Java Flight Recorder) before tuning GC settings.

💬 Frequently Asked Questions

What is Java Flight Recorder (JFR)?

JFR is a low-overhead profiling and monitoring tool built into the JDK (free since Java 11). It records JVM events: GC activity, thread states, CPU usage, memory allocation, I/O. Start with: java -XX:+FlightRecorder -XX:StartFlightRecording=duration=60s,filename=recording.jfr -jar app.jar. Analyze with JDK Mission Control (JMC). It is the most powerful tool for understanding production JVM behavior.

What is the difference between Minor GC and Full GC?

Minor GC (Young GC) cleans only the Young Generation — it is fast (milliseconds) and frequent. Full GC cleans the entire heap including Old Generation — it is slow (seconds for large heaps) and should be rare. If you see frequent Full GCs, your Old Generation is filling up: either there is a memory leak, the heap is too small, or too many objects are being promoted from Young to Old.

🎯 Key Takeaways

1GC automatically frees unreachable objects — no manual memory management
2Generational GC: Young (frequent, fast Minor GC) + Old (rare, slow Major GC)
3G1GC: default Java 9+, good for 4-32GB heaps; ZGC: sub-millisecond pauses
4Set -Xms = -Xmx; enable GC logging; set HeapDumpOnOutOfMemoryError
5The best GC optimization is reducing object allocation
6Use JFR for production profiling and GC analysis

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these Core Java references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages