Garbage Collection (GC) is critical to Java performance, and the JVM provides multiple collectors optimized for different use cases. Among the most significant are CMS (Concurrent Mark-Sweep), G1 (Garbage First), ZGC, and Shenandoah. Each has unique design principles, pause time guarantees, and tuning requirements.
In this tutorial, we’ll compare these collectors, explaining how they work, when to use them, and what trade-offs they introduce.
Why GC Algorithm Choice Matters
- Determines latency vs throughput trade-offs.
- Influences application scalability and responsiveness.
- Critical in microservices, low-latency systems, and large-scale data processing.
Analogy: Choosing the right GC is like choosing the right cleaning strategy for your house—do you clean once in a while with long breaks, or continuously clean in small bursts to minimize disruption?
CMS (Concurrent Mark-Sweep)
- Goal: Reduce long pauses by doing marking concurrently.
- Approach: Uses mark-and-sweep phases with concurrent marking.
- Strengths:
- Lower pause times than Parallel GC.
- Good for applications requiring predictable responsiveness.
- Weaknesses:
- Heap fragmentation issues.
- Deprecated since Java 9, removed in Java 14.
Best Use Case: Legacy applications running on Java 8/11 with CMS enabled.
G1 GC (Garbage First)
- Default since Java 9.
- Goal: Balance throughput and low pause times.
- Approach: Region-based collector with concurrent marking and incremental compaction.
- Strengths:
- Predictable pause times.
- Handles large heaps well.
- Weaknesses:
- More tuning overhead than ZGC/Shenandoah.
Best Use Case: General-purpose workloads on modern JVMs.
ZGC (Z Garbage Collector)
- Goal: Sub-millisecond pause times, even with multi-terabyte heaps.
- Approach: Region-based, concurrent compaction with colored pointers.
- Strengths:
- Pause times under 10ms.
- Scales to heaps of 16TB+.
- Weaknesses:
- Slight throughput penalty compared to G1.
- Available in Java 11+ (production-ready since Java 15).
Best Use Case: Extremely large heap, latency-critical applications.
Shenandoah GC
- Goal: Low pause times with concurrent compaction.
- Approach: Region-based, concurrent evacuation using Brooks pointers.
- Strengths:
- Consistent low pauses (<10ms).
- Strong for cloud-native workloads.
- Weaknesses:
- Slight throughput penalty.
- Initially only in Red Hat builds, now in OpenJDK.
Best Use Case: Cloud and microservices workloads needing predictable latency.
Comparison Table
Collector | Pause Times | Heap Size Support | Strengths | Weaknesses |
---|---|---|---|---|
CMS | Low, but variable | Up to ~100GB | Reduced pauses, legacy support | Fragmentation, deprecated |
G1 | Low & predictable | Large heaps | Balanced performance, default GC | Requires tuning |
ZGC | Sub-ms (<10ms) | Up to 16TB+ | Huge heaps, ultra-low latency | Lower throughput, newer GC |
Shenandoah | <10ms | Large heaps | Low latency, concurrent compaction | Slight throughput penalty |
JVM Version Tracker
- Java 8 → CMS widely used.
- Java 9 → G1 becomes default.
- Java 11 → ZGC and Shenandoah introduced.
- Java 14 → CMS removed.
- Java 17+ → ZGC and Shenandoah production-ready.
- Java 21+ → Ongoing improvements with NUMA and Project Lilliput.
Example: Enabling Collectors
# G1 (default)
java -XX:+UseG1GC -Xmx2g MyApp
# Shenandoah
java -XX:+UseShenandoahGC -Xmx2g MyApp
# ZGC
java -XX:+UseZGC -Xmx2g MyApp
# CMS (Java 8/11 legacy)
java -XX:+UseConcMarkSweepGC -Xmx2g MyApp
Best Practices
- Use G1 as the default for most workloads.
- Use ZGC for massive heaps and ultra-low latency.
- Use Shenandoah for cloud-native, latency-sensitive apps.
- Avoid CMS unless running on legacy Java versions.
- Always monitor GC logs with
-Xlog:gc*
.
Conclusion & Key Takeaways
- GC choice should be driven by pause time requirements vs throughput needs.
- CMS is legacy, G1 is the balanced default, ZGC excels at massive heaps, and Shenandoah targets low-pause cloud workloads.
- Modern Java (17+) makes ZGC and Shenandoah viable defaults for latency-sensitive systems.
FAQs
1. What is the JVM memory model and why does it matter?
It defines thread-safe interactions with memory and is crucial for concurrency.
2. How does G1 differ from CMS?
G1 compacts incrementally with predictable pauses, CMS can fragment memory.
3. When should I use ZGC over Shenandoah?
ZGC for very large heaps (TBs), Shenandoah for cloud-native apps.
4. Are there throughput penalties for low-latency GCs?
Yes, ZGC and Shenandoah trade some throughput for shorter pauses.
5. Can I still use CMS in modern Java?
Not in Java 14+; it’s removed, but still in Java 8/11.
6. What’s the default GC in Java today?
G1 since Java 9, ZGC gaining traction in newer versions.
7. How do I choose between G1 and ZGC?
If latency is critical, pick ZGC; otherwise, G1 is sufficient.
8. How do safepoints affect GC?
They’re synchronization points; all threads must stop briefly during GC.
9. What is Project Lilliput?
An effort to shrink object headers, improving GC and memory efficiency.
10. Which GC is best for microservices?
Shenandoah, due to low-pause times and efficient concurrent compaction.