Garbage Collectors

The post is a curated list of various garbage collectors available in java VM. We will also explore various use-cases for the respective collectors.

Garbage Collection is one of the most appreciated features of the Java programming language, making it suitable for all kinds of applications. The ability to automatically recover the previously allocated memory, which is no longer in use, allows the Java programmers to focus on the core business logic - a big selling point compared to other object-oriented languages like C++, where the programmer must manually manage the memory.

In most cases, the GC process can be categorized into the following:

  1. Minor GC: Targets the young generation for cleanup.
  2. Major GC: Cleans the tenured or old generation.
  3. Full GC: Both the young and old generations are targeted.

Following is the list of various Garbage Collectors that are available in Java. Each of these has its strong areas. Depending on the java version in use, we should choose a GC strategy best suited for the specific use case. The GC strategy should consider at least the following:

  1. Latency vs Throughput
  2. Heap Size
  3. Application nature - Real-time vs. Batch processing

You can also refer to this link for a list of various flags and arguments available to fine-tune the GC setup.

Please note that this is NOT an in-depth detailed post on each collector and only lists the available options.

Serial GC Collector

As the name suggests, the Serial garbage collector is a single-threaded model which halts all the application threads (Stop the world - STW event) before triggering the garbage collection. It uses the simplest form of mark-sweep-compact stages and works as a generational (young and tenured) garbage collector.

  1. It is suitable for small, standalone applications with no pause time constraints, running on single-processor machines - as it can’t take advantage of multiprocessor hardware.
  2. It is also suitable for applications with small data sets (up to approximately 100 MB).
  3. Frequent STW events and a single-threaded model make it unsuitable for multithreaded applications like an application server.

To enable serial GC, we can use the following argument: -XX:+UseSerialGC

Parallel GC Collector

Parallel GC attempts to improve the Serial GC performance and throughput by using multiple threads, specified via a command-line option –XX:ParallelGCThreads=n for young generation GC cycles. However, for the tenured (old) generation, it still uses Serial Mark and Compact algorithm.

Also, like serial GC, it issues the STW event before the GC cycles can begin to reclaim the unused memory.

It is suitable for applications with small to medium-sized datasets running on multi-core machines. If peak application performance is the priority and there are no pause time requirements or pauses of 1 second or longer are acceptable, Parallel GC can be a good choice.

To enable parallel GC, pass the following argument: -XX:+UseParallelGC

Parallel Old GC Collector

An extension to parallel GC, Parallel Old GC uses multiple threads for both tenured and the young generation.

To use parallel Old GC, pass the following argument: -XX:+UseParallelOldGC

CMS Collector

Deprecated as of java9 and entirely dropped in java14, the Concurrent Mark Sweep (CMS) collector is a mostly concurrent collector using multiple threads for the GC process. Unlike other collectors mentioned above, it is meant for applications that allow CPU sharing between application threads and GC threads.

CMS collector trigger STW event in following two cases:

  1. The first pause is to mark live objects directly reachable from the roots (for example, object references from application thread stacks and registers, static objects, and so on) and from elsewhere in the heap. This first pause is referred to as the initial mark pause.
  2. The second pause comes at the end of the concurrent tracing phase and finds objects missed by the concurrent tracing due to updates by the application threads of references in an object after the CMS collector had finished tracing that object. This second pause is referred to as the remark pause.

If response time is more important than overall throughput and garbage collection pauses must be shorter, CMS is the suggested choice of GC collector. To use CMS collector, provide the following argument: -XX:+UseConcMarkSweepGC

Garbage First Garbage Collector

The Garbage first GC - also known as G1GC, is the most commonly used garbage collector for various applications. It can be termed as a universal collector providing a balance between latency and throughput with features to meet basic requirements for most use-cases (large datasets, batch processing, transactional data, etc.).

Introduced in Java7 and made default in java9, G1GC is a mostly-concurrent low-pause collector that will provide a better overall experience, for most users, than a throughput-oriented collector such as the Parallel GC. Moreover, it deploys multiple techniques to achieve the said goals.

G1 splits the heap into virtual young and old generations with space reclamation efforts concentrating on the young generation as it is most efficient.

  1. G1GC performs a concurrent global marking phase to determine the live and dead objects throughout the heap.
  2. The completion of the marking phase is followed by the step to identify the region with the maximum number of objects marked for cleanup.
  3. Once identified, the regions are swept based on priority.

To keep STW pauses short, G1 performs space recovery incrementally in steps and parallel. Java 8u20 added another enhancement that allows identification and cleanup of duplicate String instances by referencing a single char[] globally. This can be enabled by passing the following argument: -XX:+UseStringDeduplication This feature can help to reduce the overall heap usage by up to 10%.

Although G1GC is enabled by default, you can explicitly enable it by providing -XX:+UseG1GC a VM argument.

Epsilon Garbage Collector

Java11 introduces a new no-op collector known as Epsilon Collector that handles memory allocation but does NOT implement any actual memory reclamation mechanism. In other words, it will allocate memory, as required when new objects are instantiated, but does not reclaim any space consumed by unreferenced objects.

This serves the following use cases (not limited to):

  1. Provides an initial setup for testing new GC algorithms.
  2. Stress testing for memory usage.
  3. Limits the additional overhead associated with garbage collection for very short-lived tasks like serverless setup.

In the absence of any other GC mechanism, the JVM will shut down once the available memory is exhausted.

This is an experimental feature and can be enabled by passing -XX:+UseEpsilonGC and -XX:+UnlockExperimentalVMOptions

Z Garbage Collector

The Z garbage collector was introduced as an experimental feature in Java 11. The same is now available as a production-ready product post Java15. It is a low latency, high throughput collector capable of handling datasets from 4 terabytes to 16 terabytes. It divides the heap space into logical sections known as regions which unlike G1GC, can be of different sizes.

It handles most of the GC tasks concurrently without stopping the execution of application threads for more than a few milliseconds. Additionally, pause times are independent of the heap size used, making it a suitable candidate for low latency applications.

ZGC is based on the concept of colored pointers, which is a technique that stores information in the unused bits of 64bit pointer/references itself (ZGC is not available on x32 platforms). This is possible as 64bits can reference a lot more address spaces than a system can realistically have. In addition to this, ZGC uses load barriers to keep track of heap usage.

ZGC can be enabled using -XX:+UnlockExperimentalVMOptions -XX:+UseZGC ExperimentalVMOptions flag is not required for ZGC post v15.

Shenandoah

Introduced in Java12 as an experimental feature and marked as production-ready in Java15, Shenandoah, a regional collector, aims to reduce GC pause times by doing evacuation work concurrently with the running Java threads.  Pause times with Shenandoah are independent of the heap size, meaning you will have the same consistent pause times whether your heap is 200 MB or 200 GB.

Unlike ZGC, Shenandoah is also available on x86_32 platforms (ARM32 is in dev at the time of writing this post). The collector can be enabled using -XX:+UseShenandoahGC argument.


That is all for this post. Next, I hope to write detailed posts on the individual collectors explaining the respective implementations. If you want to share any feedback, please drop me an email, or contact me on any social platforms. I’ll try to respond at the earliest. Also, please consider subscribing for regular updates.

Be notified of new posts. Subscribe to the RSS feed.