Introduction
G1GC vs ZGC vs Shenandoah is the comparison Java engineers face when moving beyond the JDK default toward explicitly low-latency garbage collection. All three collectors are available in modern OpenJDK builds. Each takes a different architectural approach to the problem of reducing GC pause times. The right choice depends on your Java version, deployment environment, latency targets, and throughput budget.
Examine all three in technical depth, compare them across the dimensions that matter for production deployments, and learn about C4 — Azul Prime’s proprietary GC — as the option for workloads where even the best of the three open-source collectors cannot meet the SLA.
What Is G1GC?
The Garbage-First Garbage Collector (G1GC) is the JDK default since Java 9. It is a generational, region-based collector that divides the heap into equal-sized regions and prioritizes collecting high-garbage-density regions first. Young generation collections are stop-the-world events running 10-100ms under typical configurations.
G1GC’s -XX:MaxGCPauseMillis is a soft target, not a ceiling. Under memory pressure, the collector will exceed the target to maintain heap stability. G1GC provides the best raw throughput of the three collectors and the most extensive tuning surface, but its STW pause times are the highest.
Key characteristics:
- Generational, region-based; collects highest-garbage regions first
- STW pauses: 10-100ms for young gen collection
- Best throughput of the three; lowest continuous CPU overhead
- Extensive tuning flags; well-understood across diverse workloads
- Default since Java 9; available in all JDK distributions
What Is ZGC?
The Z Garbage Collector (ZGC) targets sub-millisecond STW pause times through concurrent mark and relocate operations, using colored pointers and load barriers. Production-ready since Java 15, ZGC achieves near-constant pause times regardless of heap size — a 1TB heap produces the same brief STW pause as a 4GB heap.
Generational ZGC (Java 21+, enabled with -XX:+UseZGC -XX:+ZGenerational) improves throughput by applying generational theory to ZGC’s concurrent framework, making it a stronger general-purpose alternative to G1GC in Java 21+ environments. The pause times remain under 1ms but are not zero — brief root scanning STW phases persist.
Key characteristics:
- Concurrent mark and relocate using colored pointers and load barriers
- STW pauses: typically under 1ms; heap-size-independent
- Scales to multi-terabyte heaps
- Available in all OpenJDK and Oracle JDK distributions from Java 15+
What Is Shenandoah GC?
Shenandoah is Red Hat’s low-latency garbage collector, designed to perform concurrent evacuation — unlike ZGC, which requires brief STW phases for relocation setup. Shenandoah’s concurrent evacuation means it can, in theory, approach zero STW time by moving objects while application threads are running.
Available experimentally from Java 12 and in production since Java 15, Shenandoah is included in OpenJDK distributions but notably absent from Oracle JDK. This distribution constraint is a practical consideration: if your deployment targets Oracle JDK, Shenandoah is not an option.
Shenandoah uses a Brooks forwarding pointer scheme to maintain object references during concurrent evacuation, which introduces a per-object memory overhead. This additional indirection also adds CPU overhead during normal object access, making Shenandoah’s throughput cost somewhat higher than ZGC’s in many benchmarks.
Key characteristics:
- Concurrent evacuation: moves objects without requiring STW relocation phase
- STW pauses: typically under 1ms (initial mark and final update references)
- Higher CPU and memory overhead than ZGC due to Brooks pointer indirection
- Available since Java 12 (experimental), Java 15 (production), OpenJDK only
- Not available in Oracle JDK: a hard constraint for some deployments
- Excellent for environments where Red Hat/OpenJDK support is preferred
G1GC vs ZGC vs Shenandoah Comparison
| Feature | G1GC | ZGC | Shenandoah |
|---|---|---|---|
| GC Algorithm | Generational region-based | Concurrent mark + relocate | Concurrent evacuation |
| Stop-the-World Pauses | 10-100ms (young gen) | <1ms (not zero) | <1ms (not zero) |
| Latency | 10-200ms | Sub-millisecond | Sub-millisecond |
| Throughput | Highest of the three | 5-15% lower than G1GC | Higher CPU overhead than ZGC |
| Memory Overhead | Lowest of the three | Moderate | Moderate to high |
| Java Version Support | Default since Java 9 | Java 15+ (prod); 21+ gen. | Java 15+ (OpenJDK only) |
| Best For | General-purpose | Latency + large heaps | Latency (RedHat/OpenJDK env) |
Latency and Pause Times
All three collectors address STW pause times differently. G1GC accepts STW pauses of 10-100ms as the price of high throughput. ZGC reduces STW phases to brief root scanning operations under 1ms but does not eliminate them. Shenandoah pushes concurrent operation further, with concurrent evacuation that can overlap with application execution, but still requires brief STW phases for initial mark and final reference updates.
In practice, ZGC and Shenandoah produce similar pause profiles for most workloads — both typically under 1ms. Shenandoah’s concurrent evacuation gives it a slight architectural edge in theory, but the implementation overhead of Brooks pointers often offsets this advantage in practice. Both are generally better than G1GC for latency-sensitive workloads.
Heap size behavior differs across all three. G1GC pauses grow with live set — a 500GB heap produces much longer pauses than a 16GB heap. ZGC pause times are nearly heap-size-independent due to its concurrent concurrent architecture. Shenandoah similarly provides heap-size-independent pause times, though its overhead per object is higher.
All three collectors still have non-zero STW pauses. For workloads where the GC must be truly transparent — where any pause, however brief, violates SLAs — none of the three open-source collectors is sufficient.
Throughput and Memory Trade-offs
G1GC leads on throughput. Shenandoah has the highest overhead of the three due to the Brooks forwarding pointer per object and the concurrent evacuation overhead. ZGC falls between them, with lower per-object overhead than Shenandoah but higher continuous CPU cost than G1GC.
Generational ZGC (Java 21+) substantially improves ZGC’s throughput position by concentrating concurrent GC work on short-lived objects. For Java 21+ deployments, Generational ZGC and G1GC are much closer in throughput than the legacy ZGC vs G1GC comparison would suggest.
Memory overhead: G1GC has the lowest net memory cost. ZGC’s colored pointer metadata adds overhead at scale. Shenandoah’s Brooks pointer per object adds a fixed per-object cost that is noticeable on workloads with large numbers of live objects.
Deployment constraints shape the decision: if you are on Oracle JDK, Shenandoah is not available. If you are on Java 8 or Java 11, neither ZGC nor Shenandoah is production-ready; G1GC is the only option. For Java 21+ deployments on OpenJDK, all three are viable.
Introducing C4: The Truly Pauseless Alternative
G1GC, ZGC, and Shenandoah all share a fundamental property: they have stop-the-world phases. Shenandoah minimizes them with concurrent evacuation. ZGC minimizes them with concurrent relocation. G1GC accepts them. But none eliminates them.
Azul Prime’s C4 GC (Continuously Concurrent Compacting Collector) is designed to eliminate stop-the-world pauses. The Loaded Value Barrier (LVB) — C4’s core mechanism — handles concurrent relocation without any STW phase. Application threads are designed to avoid pausing for garbage collection.
For workloads where the answer to ‘how short do GC pauses need to be?’ is ‘zero,’ C4 is built for exactly this requirement. ZGC and Shenandoah offer excellent low-latency characteristics, but they are fundamentally still pause-minimization solutions. C4 is a pause-elimination solution.
C4 and Azul Prime (Zing)Azul Prime additionally offers:
- Self-tuning operation — no GC flags to configure; the collector adjusts dynamically to workload
- Multi-terabyte heap support with consistent sub-millisecond response times across all heap sizes
- ReadyNow! technology — eliminates JIT warmup latency spikes at startup
- Falcon JIT compiler — more aggressive optimizations than HotSpot C2, improving steady-state throughput
- Drop-in compatibility with standard Java applications — no code changes required
Financial trading firms, real-time risk platforms, and high-frequency microservices where GC pause variability is a business risk — not just an engineering concern — find that C4’s pause-elimination design is built to meet that bar. It is not a GC tuning decision; it is a JVM platform decision.
Frequently Asked Questions
What is the main difference between ZGC and Shenandoah?
Both target sub-millisecond GC pauses, but their concurrent mechanisms differ. ZGC uses colored pointers and load barriers for concurrent relocation, with brief STW phases for root scanning. Shenandoah uses Brooks forwarding pointers for concurrent evacuation, also with brief STW phases for initial mark and reference updates. Shenandoah’s approach theoretically allows more work to happen concurrently, but the Brooks pointer overhead often results in higher CPU and memory cost compared to ZGC.
Is Shenandoah available in Oracle JDK?
No. Shenandoah is not included in Oracle JDK distributions. It is available in OpenJDK builds and distributions based on OpenJDK, including Red Hat’s builds and Adoptium. If you must use Oracle JDK, ZGC is the low-latency alternative.
Which GC has the lowest memory overhead?
G1GC has the lowest memory overhead of the three. ZGC’s colored pointer metadata adds overhead per pointer. Shenandoah adds a Brooks forwarding pointer per live object. For memory-constrained environments, G1GC is the most efficient. However, low-latency requirements typically take precedence over memory overhead in the contexts where ZGC or Shenandoah are considered.
When should I choose Shenandoah over ZGC?
Shenandoah is a reasonable choice when you are on a Red Hat OpenJDK platform that provides strong Shenandoah support, when your workload pattern benefits from concurrent evacuation, or when your operations team has existing Shenandoah expertise. For most new Java 21+ deployments, Generational ZGC is the more broadly supported low-latency choice.
What does ‘truly pauseless’ mean for C4?
Truly pauseless is designed to mean the elimination of stop-the-world pauses — no phase of the GC cycle is intended to halt application threads. ZGC and Shenandoah are ‘nearly pauseless’: their STW phases are very brief (under 1ms), but they exist. C4’s Loaded Value Barrier is designed to eliminate STW phases, so application threads are not intended to stop for GC. This is a qualitative difference, not a quantitative one.
Conclusion
G1GC, ZGC, and Shenandoah represent a spectrum from throughput-optimized to latency-optimized garbage collection. G1GC is the right default for most workloads. ZGC is the right escalation for latency-sensitive Java 15+ applications, with Generational ZGC in Java 21 narrowing the throughput cost. Shenandoah is a viable alternative in OpenJDK environments, particularly in Red Hat ecosystems.
All three share the fundamental limitation of non-zero STW pauses. When that limitation is unacceptable — when your SLAs require pauseless GC at arbitrary heap scale — Azul Prime’s C4 GC is designed to address gaps the open-source alternatives leave unresolved.