Back to CI/CD Pipeline
Q13Question 13 of 56

Comparing modern GC algorithms: Serial, Parallel, G1, ZGC, Shenandoah: pause model, best-fit heap/workload, and defaults by Java version

Key Concepts
  • Serial GC: single-threaded, full stop-the-world, best for tiny heaps and single-core containers.
  • Parallel GC: multi-threaded stop-the-world, maximum throughput, best for batch processing (default through Java 8).
  • G1: region-based, mostly concurrent, bounded pause target, balanced general-purpose choice (default since Java 9).
  • ZGC: colored pointers and load barriers, sub-millisecond pauses independent of heap size, best for large-heap latency-critical services.
  • Shenandoah: Brooks forwarding pointers, concurrent compaction, similar goals to ZGC by a different mechanism.
  • Collector choice must be measurement-driven: define a latency SLO, measure actual pauses, then decide.

Definition

Pick Serial for tiny heaps, Parallel for throughput batch jobs, G1 for balanced large-heap general use (the modern default), and ZGC or Shenandoah when you need consistently tiny pauses on huge heaps.
  • Serial GC – single-threaded, full stop-the-world collection; lowest overhead, best for tiny heaps and single-core environments.
  • Parallel GC (Throughput) – multi-threaded stop-the-world collection; maximizes total throughput, best for batch work that tolerates pauses (default through Java 8).
  • G1 (Garbage First) – region-based, mostly concurrent; bounded, predictable pauses via a pause-time target, best for large server heaps (default since Java 9).
  • ZGC – colored pointers and load barriers; sub-millisecond pauses independent of heap size, scales to terabytes (production-ready Java 15+).
  • Shenandoah – Brooks forwarding pointers and concurrent compaction; similar low-pause goals to ZGC by a different mechanism (Red Hat).

Imagine cleaning the paperwork clutter in National Bank of India's offices. Serial is one cleaner who makes everyone stop: fine for a tiny branch. Parallel is a whole cleaning crew that still pauses everyone, but finishes fast: good for overnight batch processing. G1 cleans one room at a time, mostly while staff keep working, pausing everyone only briefly. ZGC and Shenandoah are near-invisible cleaners who tidy around staff without ever stopping them: exactly what you want for a trading system where a one-second freeze is unacceptable.

Understanding the Concepts

What is garbage collection and why does the collector matter?

Garbage collection reclaims heap memory occupied by objects no longer reachable. The central tension is that doing this safely can require pausing the application: a stop-the-world pause. Different collectors manage that pause differently.

BASH
# Check which GC is active
java -XX:+PrintCommandLineFlags -version 2>&1 | grep UseGC
TEXT
# Java 8 default:
-XX:+UseParallelGC

# Java 17+ default:
-XX:+UseG1GC

How does Serial GC work?

Serial GC uses a single thread and performs a full stop-the-world pause for every collection. It has the lowest overhead and memory footprint.

BASH
# Enable Serial GC for a small container
java -XX:+UseSerialGC \
     -Xmx128m \
     -jar account-validator.jar
TEXT
Best fit: heap < 200 MB, single-core containers
Pause: grows with heap size
Default: auto-selected for single-CPU environments

How does Parallel GC work?

Parallel GC uses many threads for stop-the-world collection to maximize total throughput. It was the default through Java 8.

BASH
# Enable Parallel GC for a batch job
java -XX:+UseParallelGC \
     -XX:ParallelGCThreads=8 \
     -Xmx4g \
     -jar nightly-settlement.jar
TEXT
Best fit: batch processing, analytics, throughput-bound work
Pause: shorter than Serial for same heap, but still stop-the-world
Default: Java 8 and earlier

How does G1 work?

G1 divides the heap into many equal-sized regions and collects the regions with the most garbage first, aiming for a bounded pause-time target.

BASH
# G1 with a 100ms pause target
java -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=100 \
     -Xmx8g \
     -jar transfer-service.jar
TEXT
Best fit: server applications, 2-64 GB heaps
Pause: bounded by MaxGCPauseMillis (default 200ms)
Default: Java 9 onward

How do ZGC and Shenandoah achieve sub-millisecond pauses?

Both do marking and relocation concurrently with the application. ZGC uses colored pointers and load barriers; Shenandoah uses Brooks forwarding pointers.

BASH
# Enable ZGC (generational by default from Java 23)
java -XX:+UseZGC \
     -Xmx64g \
     -jar fraud-scoring.jar

# Enable Shenandoah (generational mode in Java 25)
java -XX:+UseShenandoahGC \
     -XX:ShenandoahGCMode=generational \
     -Xmx32g \
     -jar card-auth.jar
TEXT
Best fit: latency-critical services, large heaps (GB to TB)
Pause: sub-millisecond, independent of heap size
Trade-off: extra CPU and memory overhead for concurrent work

How do all collectors compare?

| Collector | Threads | Pause model | Heap range | Default | Best for |
|---|---|---|---|---|---|
| Serial | 1 | Full STW | < 200 MB | Single-core | Tiny containers |
| Parallel | Many | Full STW | 1-8 GB | Java 8 | Batch, throughput |
| G1 | Many | Bounded STW | 2-64 GB | Java 9+ | General server |
| ZGC | Concurrent | Sub-ms | 1 GB-16 TB | Opt-in | Low-latency |
| Shenandoah | Concurrent | Sub-ms | 1-64 GB | Opt-in | Low-latency |

What are the defaults by Java version?

TEXT
Java 8:   Parallel GC (throughput-oriented)
Java 9+:  G1 GC (balanced)
Java 15:  ZGC production-ready (opt-in)
Java 21:  Generational ZGC opt-in
Java 23:  Generational ZGC default mode
Java 24:  Non-generational ZGC removed (JEP 490)
Java 25:  Generational Shenandoah (JEP 521)
BASH
# Verify the default collector on your JVM
java -XX:+PrintFlagsFinal -version 2>&1 | grep -i "Use.*GC "

What are common mistakes?

The most common mistake is choosing a collector without measuring. Fashion-driven choices waste resources.

BASH
# WRONG: using ZGC for a batch job that tolerates pauses
# ZGC's concurrent overhead reduces throughput for no benefit
java -XX:+UseZGC -Xmx4g -jar nightly-settlement.jar

# CORRECT: Parallel GC maximizes throughput for batch
java -XX:+UseParallelGC -Xmx4g -jar nightly-settlement.jar
BASH
# WRONG: default G1 for a service that needs sub-ms pauses
java -Xmx64g -jar fraud-scoring.jar  # G1 pauses grow with heap

# CORRECT: ZGC for sub-ms pauses on large heaps
java -XX:+UseZGC -Xmx64g -jar fraud-scoring.jar

What should a senior engineer know?

Collector choice must be measurement-driven. Define a latency SLO, measure the actual pause behaviour under realistic load, and only then decide. At National Bank of India, the nightly settlement uses Parallel GC for maximum throughput, the Transfer API uses G1 for bounded pauses, and the fraud-scoring engine uses ZGC for sub-millisecond pauses on a 64 GB heap.

BASH
# Enable GC logging for measurement
java -Xlog:gc*:file=gc.log:time,level,tags \
     -XX:+UseG1GC \
     -Xmx8g \
     -jar transfer-service.jar

# Analyze GC pauses
grep "Pause" gc.log | awk '{print $NF}' | sort -n | tail -5

Visual Overview

DIAGRAM
  Collector spectrum: throughput ←──────────────→ latency

  Serial     Parallel        G1           ZGC / Shenandoah
  ─────────  ───────────     ──────────   ──────────────────
  1 thread   many threads    region-based concurrent mark+relocate
  full STW   full STW        bounded STW  sub-ms STW
  tiny heap  batch work      general      large heap, low latency

  Java version defaults:
  ┌──────────┐  ┌──────────┐  ┌──────────┐
  │ Java 8   │  │ Java 9+  │  │ Java 15+ │
  │ Parallel │  │ G1       │  │ ZGC opt  │
  └──────────┘  └──────────┘  └──────────┘

Key Takeaway

The JVM offers several garbage collectors that trade off throughput, pause time, and resource use differently. Serial is for tiny heaps. Parallel maximizes throughput for batch jobs. G1, the default since Java 9, gives balanced bounded pauses for general server work. ZGC and Shenandoah deliver sub-millisecond pauses independent of heap size for latency-critical services.

The clean anchor for defaults is that Parallel was the default through Java 8 and G1 has been the default since Java 9, while ZGC and Shenandoah are opt-in. Generational ZGC became the only mode in Java 24, and Generational Shenandoah arrived in Java 25.

The judgement that separates a senior answer is that collector choice must be measurement-driven. At National Bank of India, the nightly settlement batch used Parallel, the customer-facing Transfer API used G1, and the real-time fraud engine used ZGC, each chosen based on measured latency requirements, not fashion.

Remember one sentence: G1 is the sensible modern default, and you only move to ZGC or Shenandoah when measurements show G1's pauses exceed your latency budget.

---

Share
Use ← → arrow keys to navigate