GC Explained: Collectors Overview
The current version of HotSpot JVM includes three types of garbage collectors:
– Serial Collector
– Parallel Collector
– The Mostly Concurrent Collectors
All of them are generational ones, meaning that they take advantage of the way the heap is divided.
There are three main operations which garbage collector is responsible for:
– finding objects which are no longer used
– freeing up the memory after those objects
– compacting the heap
Not all the collectors perform those operations in the same way, so let’s go through the basic information about all of them. We will cover details in separate articles.
Serial Collector
As the name suggests, the collection is performed by only one thread. Stop-the-world (STW) pauses are necessary during both Minor and Full GC.
This collector uses the mark-copy algorithm for the Young Generation, whereas the Old Generation is cleaned up using the mark-sweep-compact algorithm.
Serial GC is designed for single-threaded environments (usually client-class machines) and for relatively small heaps. It can be enabled by -XX:+UseSerialGC
flag.
Parallel (Throughput) Collector
The Young collection is parallelized by multiple threads which makes Minor GC much faster. As a result, this collector leads to shorter, but more frequent Young collection STW pauses. Since JDK 7u4, the Old Generation is also collected by multiple threads by default (and also causes stop-the-world pauses). Prior to JDK 7u4, -XX:+UseParallelOldGC
flag was required to enable parallel processing of the Old Generation. Now, both -XX:+UseParallelGC
and -XX:+UseParallelOldGC
flags enable Throughput Collector with parallel processing of both the Old and Young Generations.
This collector also uses the mark-copy algorithm in the Young Generation and mark-sweep-compact in the Old Generation, but both copy and compact phases are executed by multiple threads.
To configure the number of GC threads you can use -XX:ParallelGCThreads=X
flag. The default value is set to the number of CPU cores.
When is Parallel GC a good choice? Well, basically whenever throughput is more important than latency.
The Mostly Concurrent Collectors
They are so called low pause collectors – designed to minimize stop-the-world pauses and to keep the application as responsive as possible
Concurrent Mark and Sweep (CMS)
Minor GC is performed with multiple threads using the parallel mark-copy algorithm. All application threads are stopped then. The Old Generation is mostly collected concurrently – application threads are paused for very short periods of time when the background GC thread scans the Old Generation. The actual algorithm used during Major GC is concurrent mark-sweep. As you probably noticed, there is no “compact” after “sweep”. That’s true – Concurrent Mark and Sweep is the collector which doesn’t compact the Tenured space and thus the memory can be left fragmented. Due to lack of heap compaction, when GC is not able to fit new objects into the memory, JVM fallbacks to the serial mark-sweep-compact algorithm to defragment and compact the Old Generation. That’s when performance degradation comes – all application threads are stopped and just one single thread is responsible for cleaning and compacting the Tenured space.
As I mentioned earlier, CMS is an example of low pause collectors. It means that it’s a good choice when a latency is a primary target, not throughput – because throughput can be degraded due to increased CPU consumption (scanning the heap when application threads are running isn’t for free).
-XX:+UseConcMarkSweepGC
enables CMS collector. It used to be possible to configure CMS with a single-threaded Young Generation collection using -XX:-UseParNewGC
(notice minus before “UseParNewGC”, so by using this flag we disable Parallel New (Young) GC), but it has been deprecated in Java 8 and removed in Java 9.
G1GC
Garbage First (G1) is a new low-pause garbage collector designed to process large heaps with minimal pauses. The heap is broken down into several regions of fixed size (while still maintaining the generational nature of the heap). That kind of design allows us to get rid of long STW pauses when the entire Young or Old Generations are processed. Now, each region can be collected separately which leads to shorter, but more frequent STW pauses. G1 copies objects from one region into another, which means that the heap is at least partially compacted.
G1 uses an incremental version of the mark-sweep-compact algorithm. It can be enabled by specifying -XX:+UseG1GC
flag.
Summary
Here is a simple comparison of the collectors discussed in this article:
Collector | Multiple GC Threads | STW (Young Generation) | STW (Old Generation) | Heap Compaction | Primary Goal |
---|---|---|---|---|---|
Serial | no | yes | yes | yes | – |
Parallel | yes | yes | yes | yes | throughput |
CMS | yes | yes | only during scan | no | latency |
G1 | yes | yes | very short ones | partially | latency |
There are some other garbage collectors out there, but they are not part of HotSpot JVM. These are:
- C4 (Azul Zing JVM)
- Shenandoah
- Balanced (IBM J9 JVM)
In Java 8, a default GC for server-class machines is Parallel Collector. G1GC is going to be a default one in Java 9. Client-class machines run Serial Collector by default.
Reference: | GC Explained: Collectors Overview from our JCG partner Grzegorz Mirek at the > performant code_ blog. |