Skip to content
Back
JVM internals

Serial, Parallel and G1: The Throughput Collectors

How the classic JVM collectors work, when raw throughput wins, and what G1's region-based design changed.

Michał Artur Marciniak

3 min read

On this page
  1. Serial GC - The Solo Virtuoso
  2. Parallel GC - The Full Orchestra
  3. G1 GC - The Section Leader

Part 1 mapped the trade-off between throughput, latency and footprint. This part covers the three collectors that lean toward throughput: Serial for small heaps, Parallel for batch work, and G1, the default that balances both.

Serial GC - The Solo Virtuoso

Pauses
Long, stop-the-world
Throughput
Moderate
Heap
Up to 100 MB
GC threads
1

One Thread to Rule Them All

The Serial garbage collector is the minimalist’s choice - a single thread performs all GC work. When collection begins, the entire application pauses while this lone worker cleans house.

Use it when:

  • Running on single-core machines
  • Heap sizes under 100MB
  • Embedded systems with minimal resources
  • Testing and development environments
Shell
java -XX:+UseSerialGC -Xms512m -Xmx512m MyApp

The Serial collector is the acoustic guitar of GCs - no amplification, no effects, just pure simplicity. It doesn’t scale, but for small venues, it’s perfectly adequate.

When to choose Serial over Parallel? Surprisingly, on single-core systems, Serial often outperforms Parallel because there’s no thread coordination overhead. Multiple threads fighting for one CPU core creates more contention than a single disciplined worker.


Parallel GC - The Full Orchestra

Pauses
Long, sometimes seconds
Throughput
Very high
Heap
4 GB and up
GC threads
One per core

Throughput at All Costs

Parallel GC (also called Throughput Collector) is Serial’s multi-threaded sibling. During young generation collections, multiple threads race to clean up, dramatically reducing pause times compared to Serial. However, old generation collections still stop the world.

This collector prioritizes throughput - total work accomplished - over individual pause times. It’s the heavy metal of garbage collectors: loud, powerful, unapologetic about the noise it makes.

Use it when:

  • Batch processing and ETL jobs
  • Scientific computing
  • Any workload where total throughput matters more than individual pauses
  • Large heaps (4GB+) on multi-core systems
Shell
java -XX:+UseParallelGC -XX:ParallelGCThreads=8 -Xms8g -Xmx8g MyApp

Tuning the Parallel Collector

Shell
# Target maximum pause time (millis)
java -XX:MaxGCPauseMillis=200 ...

# Target throughput percentage (time spent in GC vs app)
java -XX:GCTimeRatio=19  # 1/(1+19) = 5% time in GC

# Explicit thread count (defaults to CPU count)
java -XX:ParallelGCThreads=16 ...

The Trade-Off

Parallel GC sacrifices latency for throughput. It will pause your application, sometimes for seconds, but when running, your application threads enjoy nearly all CPU resources. For non-interactive workloads, this is often the right choice.

Real-World Scenario: A nightly data transformation job processing terabytes of records doesn’t care about 2-second pauses. It cares about finishing by morning. Parallel GC is the workhorse here.


G1 GC - The Section Leader

Pauses
Target 200 ms by default
Throughput
High
Heap
4–16 GB
GC threads
Many, part concurrent

Region-Based Revolution

G1 (Garbage-First) represents a paradigm shift. Instead of treating the heap as two monolithic spaces (young/old), G1 divides it into many equal-sized regions - typically 1-32MB each. A region can be Eden, Survivor, Old, or Humongous (for objects larger than 50% of a region).

This regional architecture enables incremental collection - G1 can clean parts of the old generation without a full stop-the-world collection.

G1’s Core Strategy: Prioritize regions with the most garbage. Why waste time compacting regions full of live objects when others are 90% garbage?

The Collection Cycle

The G1 collection cycleState with 4 nodes and 5 connectionsAllocationEden fullOccupancy thresholdMarking doneSpace reclaimedJVM startJVM startYoung collection · STWYoung collection · STWMarking · concurrentMarking · concurrentMixed collection · STWMixed collection · STW
  1. JVM start
  2. ↓ AllocationYoung collection · STW↻ Eden full
  3. ↓ Occupancy thresholdMarking · concurrent
  4. ↓ Marking doneMixed collection · STWSpace reclaimed → Young collection · STW
Young collections run whenever Eden fills. Once the old generation crosses the occupancy threshold, G1 marks it while the application runs, then mixed collections evacuate young regions together with the old regions that hold the most garbage.

Configuration for G1

Shell
# Enable G1 (default on Java 9+ server-class machines)
java -XX:+UseG1GC -Xms8g -Xmx8g MyApp

# Target maximum pause time (default: 200ms)
java -XX:MaxGCPauseMillis=100 ...

# Region size (default: calculated based on heap, min 1MB, max 32MB)
java -XX:G1HeapRegionSize=16m ...

# Maximum tenuring threshold
java -XX:MaxTenuringThreshold=5 ...

When G1 Excels

  • Heaps 4-16GB: efficient region management
  • Predictable pauses: configurable target (though not guaranteed)
  • Mixed workloads: balances throughput and latency
  • Humongous objects: dedicated regions prevent fragmentation

The Reality Check

G1 is the default for a reason - it’s the safe choice that works reasonably well for most applications. But “reasonable” isn’t “optimal.” G1 pause times under 50ms are unrealistic. If you need sub-10ms pauses, look to ZGC or Shenandoah.

2026 Best Practice: Set -Xms equal to -Xmx for any production JVM. Heap resizing triggers full GCs and fragments memory. Pre-allocate and stay fixed.

Java 24 Improvements (JEP 475)

Java 24 brought a significant transparent optimization to G1: late barrier expansion for C2 compiler write barriers (JEP 475). This optimization moves G1 write barrier handling to a later stage in the C2 compilation pipeline, reducing compiler overhead by 10–20%. This is a transparent improvement - no configuration needed, users on Java 24+ benefit automatically.