Java Garbage Collection Cheat Sheet
A practical reference covering GC collector trade-offs, G1’s region-based layout, low-pause collectors (ZGC/Shenandoah), and how to diagnose GC behavior in production.
Collector Trade-offs
Trade-off triangle: throughput vs pause time vs memory footprint. No GC wins on all three.
Collector Throughput Pause Time Use Case
─────────────────────────────────────────────────────
Serial Low High Single-core, small heaps
Parallel High Medium Batch processing, throughput focus
CMS Medium Low(ish) Deprecated in Java 14
G1 (default) High Predictable General purpose (Java 9+)
ZGC High Sub-ms Latency-critical (Java 15+)
Shenandoah High Sub-ms Same as ZGC, different algorithm
G1 — Region-Based Heap Layout
Heap divided into ~2048 equal-sized regions (1–32 MB each). Regions can be Eden, Survivor, Old, or Humongous (large objects). G1 prioritizes regions with most garbage first — hence “Garbage First”. Mixed GC collects both young and old regions together.
ZGC & Shenandoah — Concurrent Low-Pause Collectors
Both achieve sub-millisecond pauses by doing most GC work concurrently with the application. They use load barriers to handle object references being moved while the app runs. Higher CPU overhead (~5–15%) — the cost of concurrency. Ideal for payment APIs and low-latency services.
# Enable ZGC (Java 15+ for production)
-XX:+UseZGC
-XX:SoftMaxHeapSize=4g # soft limit — ZGC tries to stay under this
# ZGC phases (all concurrent, no stop-the-world except tiny pauses):
# 1. Mark start (pause ~1ms)
# 2. Concurrent mark
# 3. Mark end (pause ~1ms)
# 4. Concurrent process references
# 5. Concurrent relocate
# 6. Concurrent remap
# Shenandoah
-XX:+UseShenandoahGC
-XX:ShenandoahGCMode=iu # incremental-update mode (default)
GC Logging & Diagnostics
Enable GC logging with -Xlog:gc* (Java 9+). Key things to look for: frequency of collections, pause duration, heap size before/after, allocation rate. A Full GC is always a red flag — it stops all threads.
# Enable GC logging
-Xlog:gc*:file=gc.log:time,uptime,level,tags
# Sample G1 log output:
[2.456s][info][gc] GC(3) Pause Young (Normal) (G1 Evacuation Pause)
[2.456s][info][gc] GC(3) Heap: 512M -> 128M (1024M)
[2.456s][info][gc] GC(3) Pause: 12.3ms
# Red flags:
# - "Full GC" → heap pressure, possible leak, or wrong GC tuning
# - Pause > MaxGCPauseMillis target consistently
# - Heap size after GC growing each cycle → leak
# - Allocation rate spiking → short-lived object pressure
# Useful tools:
# - GCViewer (open source)
# - GCEasy (web-based)
# - JDK's built-in: jstat -gcutil <pid> 1000
