Garbage Collectors In Depth
Garbage Collector Overview
Garbage collectors are concrete implementations of garbage collection algorithms. Different collectors have different focuses and are suited for different scenarios:
Young Generation Old Generation Whole-Heap Collectors
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Serial │────→│ Serial Old │ │ │
├─────────────┤ ├─────────────┤ │ │
│ ParNew │────→│ CMS │ │ │
├─────────────┤ ├─────────────┤ │ │
│ Parallel │────→│ Parallel │ │ │
│ Scavenge │ │ Old │ │ │
├─────────────┤ ├─────────────┤ ├─────────────┤
│ │ │ │ │ G1 │
├─────────────┤ ├─────────────┤ ├─────────────┤
│ │ │ │ │ ZGC │
├─────────────┤ ├─────────────┤ ├─────────────┤
│ │ │ │ │ Shenandoah │
└─────────────┘ └─────────────┘ └─────────────┘
Serial / Serial Old
The most basic and oldest collector, using a single thread for garbage collection.
How It Works
User threads ──███──pause──██████████──pause──███──
Serial ────────→ GC ←──────────────→ GC ←──
- Serial (young generation): Copying algorithm
- Serial Old (old generation): Mark-Compact algorithm
Characteristics
- Single-threaded; must stop all user threads during GC (Stop The World, STW)
- Simple implementation, low memory overhead
- Suitable for client mode or small-memory applications
# Usage parameter
-XX:+UseSerialGC # Use Serial + Serial Old together
ParNew
The multi-threaded version of Serial, using multiple GC threads for garbage collection.
Characteristics
- Multi-threaded concurrent GC (default thread count = number of CPU cores)
- Apart from multi-threading, behavior is identical to Serial
- The only young generation collector that can work with CMS
# Usage parameters
-XX:+UseParNewGC # Use ParNew
-XX:ParallelGCThreads=4 # Number of GC threads
Parallel Scavenge / Parallel Old
Parallel Scavenge (Young Generation)
Similar to ParNew, but focuses on throughput rather than pause time.
Throughput = Time running user code / (Time running user code + GC time)
GC Ergonomics (Adaptive Tuning): Parallel Scavenge dynamically adjusts young generation size, Eden/Survivor ratio, object promotion age, and other parameters based on current system conditions to meet throughput targets as closely as possible.
# Core parameters
-XX:+UseParallelGC # Use Parallel Scavenge + Parallel Old
-XX:MaxGCPauseMillis=200 # Max GC pause time target (best effort)
-XX:GCTimeRatio=99 # Throughput size (default 99, i.e., GC time = 1%)
-XX:+UseAdaptiveSizePolicy # Adaptive tuning (enabled by default)
-XX:ParallelGCThreads=4 # Number of GC threads
Parallel Old (Old Generation)
The old generation counterpart of Parallel Scavenge, using the Mark-Compact algorithm. Available since JDK 6; previously, Parallel Scavenge could only pair with Serial Old (which severely impacted throughput).
CMS (Concurrent Mark Sweep)
CMS targets the shortest collection pause time, based on the Mark-Sweep algorithm, suitable for applications sensitive to response time.
Four Phases
1. Initial Mark (STW) ──Brief pause, mark objects directly reachable from GC Roots
2. Concurrent Mark ──Concurrent with user threads, perform GC Roots Tracing
3. Remark (STW) ──Brief pause, correct references changed during concurrent marking (incremental update)
4. Concurrent Sweep ──Concurrent with user threads, sweep unmarked objects
User threads ──███──pause──████████████████──pause──████████████████──
CMS ────→Initial──→ Concurrent ──→Remark──→ Concurrent ──→
Mark Mark Sweep
Pros and Cons
Pros:
- Concurrent collection, short pause times
- Suitable for web applications with strict response time requirements
Cons:
- CPU-sensitive: Concurrent phases consume CPU; default GC thread count = (CPU cores + 3) / 4
- Floating garbage: New garbage generated by user threads during concurrent sweep can only be collected in the next GC
- Memory fragmentation: Mark-Sweep algorithm causes fragmentation; may trigger Serial Old compaction
- Concurrent Mode Failure: Insufficient reserved space in old generation, degrades to Serial Old with full STW
# CMS parameters
-XX:+UseConcMarkSweepGC # Use CMS
-XX:CMSInitiatingOccupancyFraction=75 # Trigger GC when old gen is 75% full
-XX:+UseCMSCompactAtFullCollection # Compact after Full GC
-XX:CMSFullGCsBeforeCompaction=5 # Compact after 5 Full GCs
-XX:ConcGCThreads=2 # Concurrent GC thread count
-XX:+CMSParallelRemarkEnabled # Parallel remark
CMS Deprecation
- Marked as Deprecated in JDK 9
- Officially removed in JDK 14
- Migration to G1 is recommended
G1 (Garbage-First)
G1 is the default collector since JDK 9, designed for server-side applications, balancing throughput and pause time.
Region-Based Memory Layout
G1 breaks away from traditional physical generational separation, dividing the heap into equal-sized Regions (1–32MB, default 2048):
┌────┬────┬────┬────┬────┬────┬────┬────┐
│ E │ S │ O │ H │ E │ O │ E │ S │
├────┼────┼────┼────┼────┼────┼────┼────┤
│ O │ E │ E │ O │ E │ S │ O │ E │
└────┴────┴────┴────┴────┴────┴────┴────┘
E = Eden S = Survivor O = Old H = Humongous
Each Region can be Eden, Survivor, Old, or Humongous (large objects). G1 retains the generational concept, but the young and old generations are no longer contiguous memory regions.
Collection Modes
- Young GC: Collects all Eden and Survivor Regions
- Mixed GC: Collects all young generation Regions + selected old generation Regions (prioritizing those with high reclaim value)
- Full GC: Degrades to single-threaded whole-heap collection (should be avoided)
Workflow
1. Young GC ── Collect young generation (STW, multi-threaded parallel)
2. Concurrent Mark ── Similar to CMS's four phases
3. Mixed GC ── Select Regions to collect based on pause prediction model
Key Features
- Predictable pauses:
-XX:MaxGCPauseMillis=200sets the target pause time; G1 predicts and selects which Regions to collect based on historical data - Garbage-First: Prioritizes collecting Regions with the most garbage
- No fragmentation: Overall based on Mark-Compact, locally (between Regions) based on Copying algorithm
- Large object handling: Objects larger than half a Region size are directly allocated in Humongous Regions
# G1 parameters
-XX:+UseG1GC # Use G1
-XX:MaxGCPauseMillis=200 # Target pause time (default 200ms)
-XX:G1HeapRegionSize=4m # Region size
-XX:InitiatingHeapOccupancyPercent=45 # Heap occupancy to trigger concurrent marking
-XX:G1MixedGCCountTarget=8 # Mixed GC count target
-XX:G1MixedGCLiveThresholdPercent=85 # Region live rate below this threshold is collected
ZGC
ZGC (Z Garbage Collector) is a low-latency collector introduced in JDK 11, targeting GC pauses under 1ms (under 10ms prior to JDK 16).
Core Technologies
1. Colored Pointers
Borrows several bits from 64-bit pointers to store GC metadata, avoiding modifications to the object header:
63 42 41 40 39 38 37 0
┌──────────┬──┬──┬──┬──┬──┬─────────────────────┐
│ Unused │R0│R1│F │R │P │ Object Address │
└──────────┴──┴──┴──┴──┴──┴─────────────────────┘
↑ ↑ ↑ ↑ ↑
Remap1 Remap Finalized Remapped
└──┘ Pinning
Relocation
2. Load Barrier
Performs a check when an object reference is loaded. If the pointer color indicates the object has been moved, the reference is automatically corrected:
Object o = obj.field; // Load barrier: check o's pointer color
// If o is marked as "relocated", correct to the new address
3. Concurrent Compaction
ZGC achieves true concurrent compaction — objects can be moved while user threads continue running. Through the combination of load barriers and colored pointers, user threads always access the correct address.
Characteristics
- STW time < 1ms, independent of heap size
- Supports TB-scale heap memory
- Concurrent marking, concurrent relocation, concurrent compaction
- Non-generational (prior to JDK 21), whole-heap collection every time
- JDK 21 introduces Generational ZGC, significantly improving young generation collection efficiency
# ZGC parameters
-XX:+UseZGC # Use ZGC
-XX:ZCollectionInterval=0 # GC interval (0 = adaptive)
-XX:ZAllocationSpikeTolerance=2 # Allocation spike tolerance
-XX:+UnlockDiagnosticVMOptions # Unlock diagnostic options
-XX:+ZStatisticsForceTrace # Force trace statistics
-XX:+UseZGC -XX:+ZGenerational # JDK 21+ Generational ZGC
Shenandoah
Shenandoah is a low-latency collector developed by RedHat, with similar goals to ZGC but a different implementation.
Core Technology
Brooks Pointer: An extra pointer is added to the header of each object, pointing to the object itself. When an object is moved, only the Brooks Pointer needs to be updated; old references find the new address through indirect forwarding.
Before move: reference → [Object | Brooks Pointer → self]
After move: reference → [Old location | Brooks Pointer → new address] → [Object | Brooks Pointer → self]
Characteristics
- GC pause independent of heap size
- Uses both load barriers and write barriers (slightly higher overhead than ZGC’s pure load barrier)
- Has a generational mode (JDK 22+)
- Available in OpenJDK, not default in Oracle JDK
# Shenandoah parameters
-XX:+UseShenandoahGC # Use Shenandoah
-XX:ShenandoahGCHeuristics=adaptive # Heuristic algorithm
Collector Selection Strategy
| Scenario | Recommended Collector | Reason |
|---|---|---|
| Client / small memory | Serial | Simple and efficient, no thread overhead |
| Batch processing / background computation | Parallel Scavenge + Parallel Old | Maximize throughput |
| Web service (JDK 8) | ParNew + CMS | Low pause |
| Web service (JDK 9–14) | G1 | Balance throughput and latency |
| Low-latency requirements (JDK 11+) | ZGC | Sub-millisecond pauses |
| Low-latency requirements (non-Oracle JDK) | Shenandoah | Sub-millisecond pauses |
Default Collectors by Version
| JDK Version | Default Collector |
|---|---|
| JDK 8 | Parallel Scavenge + Parallel Old |
| JDK 9 – 20 | G1 |
| JDK 21+ | G1 (ZGC still requires manual enabling) |
Summary
This chapter covered various garbage collectors from Serial to ZGC in detail. Choosing a collector requires balancing throughput and latency: the Parallel family emphasizes throughput, CMS/G1 balances latency, and ZGC/Shenandoah pursues ultra-low latency. The next chapter will cover how to analyze collector behavior through GC logs.
Comments