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
java -XX:+UseSerialGC -Xms512m -Xmx512m MyAppThe 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
java -XX:+UseParallelGC -XX:ParallelGCThreads=8 -Xms8g -Xmx8g MyAppTuning the Parallel Collector
# 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
- JVM start
- ↓ AllocationYoung collection · STW↻ Eden full
- ↓ Occupancy thresholdMarking · concurrent
- ↓ Marking doneMixed collection · STWSpace reclaimed → Young collection · STW
Configuration for G1
# 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
-Xmsequal to-Xmxfor 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.