Which GCs minimize stop-the-world pauses
Think of a busy international airport terminal:
🟢 Junior Level
Low-Latency Garbage Collectors are specialized JVM engines designed to minimize or virtually eliminate Stop-The-World (STW) pauses by executing memory marking, reference updating, and object relocation concurrently with live application threads.
Real-World Analogy
Think of a busy international airport terminal:
- Traditional Maintenance (Parallel GC): Periodically closes the entire terminal, turns off all flight boards, and denies passenger boarding for 30 minutes while a huge custodial team vacuums every gate. High throughput, terrible passenger delay.
- Scheduled Windows (G1 GC): Cleans during scheduled 5-minute passenger boarding lulls gate by gate. Pauses are brief and predictable, but planes still briefly pause boarding.
- Automated Robotic Maintenance (ZGC & Shenandoah): Silent robotic scrubbers vacuum and polish the floors between walking passengers. Boarding never stops, flight boards never shut off, and passengers experience zero delays ($< 1\text{ ms}$), though the robots consume extra terminal electricity (5–15% CPU overhead).
The Low-Latency Collector Tier
Stop-The-World Pause Duration Comparison:
┌────────────────────────────────────────────────────────┐
│ Parallel GC: ████████████████████████ (500ms - 5000ms)│
│ G1 GC: ████ (20ms - 200ms) │
│ Shenandoah: ▌ (1ms - 10ms, often < 1ms) │
│ ZGC: ▌ (< 1.0ms guaranteed across 16 TB!) │
└────────────────────────────────────────────────────────┘
- ZGC (
-XX:+UseZGC): Oracle’s flagship ultra-low latency collector. Guarantees sub-millisecond ($< 1.0\text{ ms}$) pauses on heaps up to 16 Terabytes. - Shenandoah (
-XX:+UseShenandoahGC): OpenJDK / Red Hat alternative. Delivers $< 10\text{ ms}$ (typically $< 1\text{ ms}$) pauses via concurrent evacuation while supporting Compressed OOPs below 32 GB. - G1 GC (
-XX:+UseG1GC): The default general-purpose collector. Provides predictable, soft real-time pause targets (20–200 ms) by performing incremental mixed collections.
🟡 Middle Level
Comprehensive Architectural Strategy Matrix
| Metric / Dimension | G1 GC | Shenandoah GC | ZGC (Generational Java 21+) | Parallel GC (Benchmark) |
|---|---|---|---|---|
| Max STW Pause Target | 20 ms – 200 ms | 1 ms – 10 ms | $< 1.0\text{ ms}$ | 500 ms – 10,000 ms |
| Pause vs. Heap Scale | Grows with live data | Independent of heap size | Independent of heap size | Scales with total heap |
| Marking Phase | Concurrent (SATB) | Concurrent (SATB) | Concurrent | Stop-The-World |
| Evacuation (Compacting) | Stop-The-World | Concurrent | Concurrent | Stop-The-World |
| JIT Barrier Type | Write Barrier (on store) | Load-Reference Barrier | Load Barrier + Color Bits | None (Zero barrier tax) |
| Compressed OOPs | Supported ($< 32\text{ GB}$) | Supported ($< 32\text{ GB}$) | Not Supported (64-bit only) | Supported ($< 32\text{ GB}$) |
| Throughput Penalty | Baseline (~0–2%) | -5% to -12% | -5% to -12% | +10% to +20% (Fastest) |
| Maximum Heap Tested | ~64 GB | ~100 GB+ | Up to 16 Terabytes | Hundreds of Gigabytes |
Why Read Barriers Cost More CPU Than Write Barriers
The reason ZGC and Shenandoah pay a 5–15% application throughput tax compared to G1 lies in CPU instruction frequency:
┌────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────┐
│ Write Barrier (G1 GC) │ Load Barrier (ZGC / Shenandoah) │
├────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────┤
│ • Triggered ONLY on pointer stores: `obj.field = val;` │ • Triggered on EVERY pointer read: `val = obj.field;` │
│ • Pointer writes account for only ~10% of memory │ • Pointer reads account for ~90% of all object │
│ operations in typical Java business code. │ operations in typical enterprise applications! │
│ • Overhead: ~1–2 CPU clock cycles. │ • Overhead: Checking color/forwarding bits on reads │
│ │ consumes 5%–15% of total CPU cycles continuously. │
└────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────┘
The Allocation Stall Threat
In concurrent collectors (ZGC, Shenandoah), background worker threads mark and evacuate memory while application threads (Mutators) continue allocating new objects.
If the application’s allocation rate exceeds the speed at which background GC threads can reclaim memory:
- Available free heap headroom drops to zero before the concurrent cycle completes.
- An application thread attempting to execute
new Object()enters an Allocation Stall: the JVM forcibly suspends the allocating thread, putting it to sleep until background threads free memory. - The Result: The application experiences sudden, severe latency spikes (hundreds of milliseconds to seconds), negating the low-latency guarantees of ZGC or Shenandoah.
🔴 Senior Level
Architectural Decision Framework: Choosing the Right Collector
[ What is your Primary SLA Metric? ]
│
┌───────────────────────┴───────────────────────┐
▼ (Throughput / Total Batch Execution Speed) ▼ (Latency / Response Time SLA)
[ Parallel GC ] │
- Big Data (Spark/Hadoop) │
- Batch ETL crunching │
- Zero barrier CPU tax │
┌───────────────────┴───────────────────┐
▼ (SLA: 50ms - 200ms) ▼ (SLA: < 10ms / Hard Low-Latency)
[ G1 GC ] │
- Default for Spring Boot │
- Heaps: 2 GB - 32 GB │
- Max memory efficiency │
┌───────────────────┴───────────────────┐
▼ (Linux x64/ARM / Large Heaps) ▼ (Alpine musl / Heaps < 32GB)
[ Generational ZGC ] [ Shenandoah GC ]
- Heaps up to 16 TB - Supports Compressed OOPs
- Sub-millisecond pauses - Minimal container images
When NOT to Choose ZGC or Shenandoah
Selecting ZGC or Shenandoah is an explicit trade-off. They are counterproductive in the following scenarios:
- CPU-Saturated Environments (CPU > 80%): If your production nodes already run at 80–90% CPU utilization, introducing Load Barriers and background GC worker threads will starve application mutators, degrading overall application response times.
- Small Heaps ($< 4\text{ GB}$): In small containers, ZGC’s lack of Compressed OOPs expands all references from 4 to 8 bytes, wasting up to 30% of usable heap capacity. G1 GC is far more compact and memory-efficient.
- Non-Interactive Batch Workloads: In analytical batch processing, users do not care about 2-second pauses; they care about when the job finishes. Parallel GC finishes large batch jobs 15%–20% faster because it dedicates 100% of CPU cycles to computation without runtime barrier penalties.
Production Tuning Recipes
1. Ultra-Low Latency Profile (Java 21+ Generational ZGC):
# Guarantees sub-millisecond pauses on large heaps
-XX:+UseZGC
-XX:+ZGenerational
-XX:ConcGCThreads=4 # Background worker threads (typically cores / 4)
-XX:SoftMaxHeapSize=12g # Allows JVM to return unused memory to host OS
-Xms16g -Xmx16g
2. Balanced Enterprise Microservice Profile (G1 GC):
# Default balanced performance for web APIs
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100 # Tighter target than default 200ms
-XX:+G1UseAdaptiveIHOP # Dynamic concurrent marking start
-XX:+UseStringDeduplication # Saves 10-25% heap memory on duplicate strings
-Xms8g -Xmx8g
4 Tricky Questions
1. Why can ZGC and Shenandoah achieve sub-millisecond pause times while G1 GC typically pauses for 50–200 milliseconds?
Answer: The primary reason G1 GC pause times scale into the tens or hundreds of milliseconds is that G1 performs its Evacuation Phase (physically copying surviving objects from the Collection Set to clean regions and updating object references) while all application mutator threads are stopped in a Stop-The-World pause. In contrast, ZGC and Shenandoah execute the evacuation and pointer-updating phases concurrently in the background while application threads continue reading and writing objects. STW pauses in ZGC and Shenandoah are restricted to thread stack scanning and phase handshakes, which take less than 1 millisecond regardless of whether the heap is 10 GB or 10 TB.
2. Why do Load Barriers in ZGC and Shenandoah impose a higher CPU throughput penalty than Write Barriers in G1 GC?
Answer: In standard software applications, memory reads outnumber memory writes by a ratio of roughly 9:1. G1 GC utilizes JIT-compiled Write Barriers, which execute only when an object field is modified (obj.field = value). Because pointer mutations represent only ~10% of memory operations, G1’s barrier overhead is minimal (1–2 CPU clock cycles). In contrast, ZGC and Shenandoah utilize Load Barriers, which must intercept every single pointer dereference (value = obj.field). Because reads account for ~90% of memory traffic, constantly checking pointer metadata bits (or resolving forwarding pointers) consumes 5% to 15% of total CPU cycles, reducing overall application throughput.
3. What is an Allocation Stall in low-latency collectors, and how do you resolve it?
Answer: An Allocation Stall occurs when the application’s allocation rate outpaces the speed at which concurrent GC worker threads can mark, evacuate, and recycle memory regions. When free heap memory is completely exhausted, threads attempting to allocate new objects (new) are forcibly suspended in an operating system sleep loop until background threads reclaim sufficient headroom. Allocation stalls cause catastrophic latency spikes, destroying the sub-millisecond SLA. It is resolved by:
- Upgrading to Generational ZGC (Java 21+), which recycles Young Gen objects at high speed.
- Increasing total heap headroom (
-Xmx) to provide an allocation buffer. - Increasing the number of concurrent GC threads via
-XX:ConcGCThreads. - Optimizing application code to eliminate excessive object churn.
4. Why is Parallel GC often a superior architectural choice compared to ZGC for offline batch analytics and big data processing?
Answer: In offline batch processing (e.g., ETL jobs, machine learning training, Apache Spark pipelines, and financial end-of-day reconciliations), the primary business metric is total throughput (how quickly a massive computation completes) rather than interactive latency (how fast an individual request returns). Low-latency collectors like ZGC pay a permanent 5%–15% CPU tax due to Load Barriers and dedicate multiple CPU cores continuously to background GC threads. Parallel GC executes zero memory barriers during runtime and halts mutators entirely during collection, dedicating 100% of all CPU cores exclusively to business computation. Consequently, Parallel GC finishes batch jobs significantly faster and with lower overall energy consumption.
🎯 Interview Cheat Sheet
30-Second Elevator Pitch
“The leading collectors that minimize Stop-The-World pauses are ZGC ($< 1\text{ ms}$ pauses up to 16 TB), Shenandoah (1–10 ms pauses), and G1 GC (predictable 20–200 ms pauses). While G1 pauses mutators during object evacuation, ZGC and Shenandoah achieve sub-millisecond pauses by performing Concurrent Evacuation using JIT Load Barriers and Colored Pointers. In Java 21, Generational ZGC solved earlier allocation stall vulnerabilities, cutting CPU overhead by 50%. However, this latency reduction costs 5–15% in raw CPU throughput, meaning Parallel GC remains superior for batch computing, while G1 remains the balanced standard for general enterprise microservices.”
Key Architectural Takeaways
- The Low-Latency Leaders: ZGC ($< 1.0\text{ ms}$) and Shenandoah (1–10 ms) decouple pause duration from heap size.
- Concurrent Evacuation: The foundational mechanism: moving live objects while application threads execute concurrently.
- Read vs. Write Barriers: G1 uses Write Barriers (~10% of operations); ZGC/Shenandoah use Load Barriers (~90% of operations), incurring a 5–15% CPU tax.
- Allocation Stalls: Occur when allocation rate outpaces concurrent GC reclamation; resolved via Generational ZGC (Java 21).
- Compressed OOPs Trade-off: Shenandoah supports Compressed OOPs below 32 GB; ZGC requires 64-bit pointers on all heap sizes.
- When to Use Parallel GC: Batch ETL and analytical crunching where total job completion time matters more than pause latency.
Red Flags & Anti-Patterns
- 🚩 Claiming “ZGC has zero Stop-The-World pauses”: ZGC has micro-pauses ($< 1\text{ ms}$) for root scanning and safepoint transitions.
- 🚩 Believing ZGC is always better than Parallel GC: Ignores the 5–15% CPU throughput penalty on CPU-bound and batch workloads.
- 🚩 Configuring
-XX:MaxGCPauseMillis=1mson G1 GC: G1 cannot achieve sub-millisecond pauses; it will trigger throughput collapse and Full GCs. - 🚩 Deploying ZGC on CPU-starved systems (>85% utilization): Load barriers will compete with business logic and severely degrade performance.
- 🚩 Ignoring Allocation Stalls in production telemetry: Misdiagnosing allocation stalls as network or database latency issues.
Related Topics
- What is ZGC — Colored Pointers and Load Barrier mechanics
- What is Shenandoah GC — OpenJDK concurrent evacuation collector
- What is G1 GC — Default balanced region-based collector
- What is stop-the-world — Safepoints and pause-time impact
- What GC algorithms exist — Complete GC algorithmic taxonomy