HotSpot Classic Garbage Collectors: Serial, ParNew, Parallel, CMS and Their Trade-offs

The JVM specification intentionally leaves garbage-collection details to each vendor and version, so HotSpot alone has evolved a dozen distinct collectors. This article reviews the first six "classic" algorithms—Serial, ParNew, Parallel Scavenge, Serial Old, Parallel Old and CMS—and explains why G1 was eventually introduced.

Classifying Collectors

Axis Choices
Threading Serial (single GC thread) vs. Parallel (multiple GC threads)
Interleaving Stop-the-world (STW) vs. Concurrent (GC and app threads interleave)
Compaction Compacting (no fragmentation) vs. Non-compacting (may fragment)
Heap region Young-only, Old-only, or Whole-heap

Performance Metrics

  • Throughput = user-code-time / (user-code-time + GC-time)
  • Pause time = duration of any single STW event
  • Footprint = maximum Java heap size
  • Promptness = time between object death and reclamation

These three goals form an "impossible triangle": improving any two usually hurts the third.

Historical Timeline


1999  JDK 1.3.1   Serial GC (first collector)
2002  JDK 1.4.2   Parallel GC + CMS
2006  JDK 6       Parallel GC becomes default
2012  JDK 7u4     G1 becomes usable
2017  JDK 9       G1 becomes default
2018  JDK 11      ZGC (experimental)
2019  JDK 12      Shenandoah (experimental)
2020  JDK 14      CMS removed

Collector Matrix

Collector Target Algorithm Threads Compaction
Serial Young Copy 1 Yes
Serial Old Old Mark-Compact 1 Yes
ParNew Young Copy Parallel Yes
Parallel Scavenge Young Copy Parallel Yes
Parallel Old Old Mark-Compact Parallel Yes
CMS Old Mark-Sweep Concurrent phases No
G1 Whole heap Regional Copy/Mark-Compact Parallel + Concurrent Yes

Serial Family (Serial + Serial Old)

Serial is the simplest collector: one thread, STW, copying for the young generation and mark-compact for the old. Its still the default on 32-bit Client VMs and tiny heaps where the pause is only a few tens of milliseconds. Enable with:

-XX:+UseSerialGC

ParNew

ParNew is literally a multi-threaded copy of Serial for the young generation. It shines on multi-core machines and is the only young collector that pairs with CMS. Thread count defaults to the number of hardware threads but can be fixed:

-XX:+UseParNewGC -XX:ParallelGCThreads=8

Parallel Throughput Family (Parallel Scavenge + Parallel Old)

These collectors aim for maximum throughput, not minimum pause. Parallel Scavenge uses an adaptive sizing policy that tunes Eden/Survivor sizes, tenuring threshold and heap size automatically. Key flags:

-XX:+UseParallelGC           # young
-XX:+UseParallelOldGC        # old (auto-enabled when above is set)
-XX:MaxGCPauseMillis=200     # hint to collector
-XX:GCTimeRatio=99           # 1 % of time spent in GC
-XX:+UseAdaptiveSizePolicy   # let JVM self-tune

CMS (Concurrent Mark-Sweep)

CMS was the first low-latency collector. It performs most of its work concurrently with application threads, reducing pause times to initial-mark and remark phases only. The trade-offs are:

  • Uses mark-sweep → fragmentation
  • Needs spare heap space to avoid "concurrent-mode failure"
  • Cannot compact concurrently
  • CPU sensitive: steals cycles from the application
  • Produces "floating garbage" (objects that die after the concurrent mark)

Typical configuration:

-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=5
-XX:ParallelCMSThreads=4

CMS was deprecated in JDK 9 and removed in JDK 14.

Quick Selection Guide

  • Minimal overhead, tiny heap → Serial
  • Max throughput, batch jobs → Parallel
  • Low pause, interactive app → CMS (legacy) or G1/ZGC/Shenandoah (modern)

There is no universal collector; choose the one whose trade-offs best match your workload.

Tags: HotSpot Serial GC ParNew GC Parallel GC CMS GC

Posted on Sun, 11 Oct 2026 16:33:58 +0000 by jamescalvin