Skip to content

The GC collectors

Posted on:July 17, 2026

In the previous blog, we learned about heap structure, and that the purpose of a garbage collector is to free the application developer from manual dynamic memory management. Today, let’s take a look at the different types of collectors, each with different performance characteristics.

I. Serial Collector

The serial collector uses a single thread to perform all garbage collection work, which makes it relatively efficient because there is no communication overhead between threads.

It’s best-suited to single processor machines because it can’t take advantage of multiprocessor hardware, although it can be useful on multiprocessors for applications with small data sets (up to approximately 100 MB). The serial collector is selected by default on certain hardware and operating system configurations, or can be explicitly enabled with the option -XX:+UseSerialGC.

II. Parallel Collector

The parallel collector, also known as the throughput collector, is a generational collector similar to the serial collector. The primary difference between the serial and parallel collectors is that the parallel collector has multiple threads that are used to speed up garbage collection.

The parallel collector is intended for applications with medium-sized to large-sized data sets that are run on multiprocessor or multithreaded hardware. You can enable it by using the -XX:+UseParallelGC option.

III. Garbage-First (G1) Garbage Collector

G1 is the default collector since Java 9, selected by default on most hardware and operating system configurations, or explicitly enabled with -XX:+UseG1GC.

It’s a generational, mostly concurrent, region-based collector. Instead of splitting the heap into a few large contiguous generations, G1 partitions it into ~2048 equally sized regions and treats “young” and “old” as labels on regions rather than fixed locations. It does most of its marking work concurrently with the application, then evacuates a chosen subset of regions per pause — collecting the regions with the most garbage first (hence “Garbage-First”) — to keep pause times bounded. Its central promise is to meet a pause-time goal (-XX:MaxGCPauseMillis, default 200ms) with high probability while still delivering good throughput.

Want the long version? See the bonus deep dive: G1 Garbage Collector deep dive — why G1 was designed this way, the region layout, the collection cycle, concurrent marking (SATB), remembered sets, and pause-time prediction.

Pros

Cons

IV. The Z Garbage Collector (ZGC)

ZGC is a scalable, low-latency collector. It was introduced experimentally in Java 11 (JEP 333), became production-ready in Java 15 (JEP 377), and gained a generational mode in Java 21 (JEP 439). Enable it with -XX:+UseZGC (add -XX:+ZGenerational for the generational mode, which is opt-in on Java 21 and slated to become the default).

Where G1 keeps pauses bounded, ZGC aims to make them disappear: it does essentially everything concurrently with the application — marking, relocation/compaction, and reference processing — leaving only tiny, fixed-cost stop-the-world work (scanning thread roots). It achieves this using colored pointers (GC metadata stored inside the object reference) and load barriers (a check that fires when the application reads a reference, fixing up any pointer to a relocated object on the fly). The result is pause times that stay sub-millisecond and do not grow with heap size or live-set size.

Pros

Cons

ZGC’s sweet spot is latency-critical services on large heaps where even a few-hundred-millisecond G1 pause is unacceptable.

V. Side-by-side comparison

SerialParallelG1ZGC
Enable flag-XX:+UseSerialGC-XX:+UseParallelGC-XX:+UseG1GC (default)-XX:+UseZGC
Generational?Yes (young + old)Yes (young + old)Yes (regions labeled young/old)Yes, since Java 21 (+ZGenerational)
GC threadsSingle-threadedMulti-threadedMulti-threadedMulti-threaded
Young genCopying (STW)Copying (STW)Evacuation / copy (STW)Concurrent relocation
Old genMark-sweep-compact (STW)Mark-sweep-compact (STW)Evacuation — no sweepConcurrent relocation — no sweep
Reclaim mechanismSliding compaction in placeSliding compaction in placeCopy live objects out, free whole regionCopy live objects out concurrently
Concurrent work?None (fully STW)None (fully STW)Marking is concurrent; evacuation is STWAlmost everything concurrent
Compacts?YesYesYesYes
Fragmentation?NoNoNoNo
Typical pauseHighestHigh but parallelizedBounded (target-driven, 10s–100s ms)Sub-millisecond
ThroughputLow overheadGreatGoodGood
LatencyHigh pausesLong worst-case acceptableControllableUltra-low
Best forLow overhead, small containersWorkloads where long worst-case latencies are acceptableLong-lived services with heaps below ~16 GBLong-lived services with heaps above ~4 GB

The key point: only two of the four ever “sweep”

Notice the Old gen row. The name “Mark-Sweep-Compact” appears only under Serial and Parallel — and even there, the “sweep” is not a CMS-style in-place free-list build; it’s the address-computation walk of a sliding mark-compact, which is why neither fragments.

G1 and ZGC do not sweep at all. They reclaim by evacuation (a.k.a. relocation): live objects are copied out to fresh space, and the source region is then freed wholesale. Nothing is walked object-by-object to be freed in place.

So across all four: everyone compacts, nobody fragments — but the how differs fundamentally. Serial/Parallel slide-compact in place (the only two with a “sweep” phase in the name), while G1 and ZGC copy-and-free by region.

Related Posts