As a Java engineer, you might wonder which GC algorithm to use for your application. Choosing the GC algorithm is like choosing a life partner for your application. Seriously 😊, there are a lot of similarities:

  • The GC coexists with your application under the same roof (i.e., the JVM).
  • GC keeps cleaning up the mess (i.e., garbage) created by your application.
  • If the application gets aggressive and creates a lot of objects, GC will also become aggressive and reclaim them from memory.
  • Both GC and your application continuously fight for the same CPU cycles.

Just like how choosing the right life partner is essential for the happiness of your life, choosing the right Garbage Collector is essential for a high-performing application. In this post, I intend to share the pros and cons of each GC algorithm and my personal opinions on each of these algorithms (which can be subjective). It might help you choose the right GC algorithm.

Video

In this video, our Architect Ram Lakshmanan has unpacked the different GC algorithms available in the JVM, explained their unique trade-offs, and provided valuable guidance on how to select the best fit for specific application workloads.

What are the Java GC algorithms?

As of OpenJDK 22, there are 7 GC algorithms available:

  1. Serial GC
  2. Parallel GC
  3. CMS GC (Concurrent Mark & Sweep)
  4. G1 GC
  5. ZGC
  6. Shenandoah GC
  7. Epsilon GC

GC algorithms Overview

Let’s review the uniqueness, advantages, and disadvantages of each GC algorithm.

1. Serial GC

The Serial Garbage Collector is the first GC algorithm introduced in JDK. Serial GC performs all garbage collection work on a single thread. This means it stops all application threads (stop-the-world) during garbage collection, which can lead to noticeable long pauses in the application. Serial GC was the default garbage collection algorithm in early versions of Java, from Java 1.2 to 4.

Do you want to learn about optimizing Serial GC performance, check our Serial GC Tuning guide. Also are you curious how Serial GC log looks? Check out Reading & Analyzing Serial GC Logs post.

POV: Since pause times are high in Serial GC, it’s not ideal to use Serial GC in our modern-day applications. 

2. Parallel GC

Unlike Serial GC, Parallel GC utilizes multiple threads to perform garbage collection phases. This allows garbage collection work to be distributed across multiple CPU cores, reducing the impact of garbage collection pauses on application performance. Parallel GC is the default GC algorithm for the Java versions from 5 to 8.

Do you want to learn about optimizing Parallel GC performance, check our Parallel GC Tuning guide. Also are you curious how Parallel GC log looks? Check out Reading & Analyzing Parallel GC Logs post.

POV: If your focus is to achieve better GC throughput over occasional high GC pause times, you can use Parallel GC. Parallel GC is well suited for batch applications and business applications in which consistent GC pauses at regular intervals are acceptable.

3. CMS GC

The Concurrent Mark-Sweep (CMS) Garbage Collector is designed to minimize the application pause times by performing most of its garbage collection work concurrently with the application threads. Primary downside of CMS is that it does not compact the heap. Over time, this can lead to fragmentation, which may necessitate a long pausing full GC event to compact the heap and manage fragmentation. Here is a real case study of a Robotics application in a warehouse, where one CMS Full GC event took more than 5 minutes to complete. During these 5 minute windows none of the robots were performing the operations effectively causing chaos in the warehouse. Such occasional pause times may not be acceptable for several applications. 

Since the CMS algorithm became complex over a period and lacked developer support, it was deprecated in Java 9 and removed completely from Java 14.

Do you want to learn about optimizing CMS GC performance, check our CMS GC Tuning guide. Also are you curious how CMS GC log looks? Check out Reading & Analyzing CMS GC Logs post.

POV: Since the CMS GC algorithm is removed from Java 14, you cannot use this algorithm on higher versions of Java. It’s risky to run on a GC algorithm which is deprecated and removed. Even in lower versions of Java, unless there is a specific need, I wouldn’t use the CMS algorithm for my application.

4. G1 GC 

The Garbage First (G1) Garbage Collector is an adaptive algorithm. G1 divides the heap into equal-sized regions, allowing for flexible and efficient memory management. It performs most operations concurrently, reducing stop-the-world pauses. G1 aims to provide the best balance between latency and throughput. It works fairly well as long as your heap size is not very large. G1 GC has become the default GC algorithm since java 9.

Do you want to learn about optimizing G1 GC performance, check our G1 GC Tuning guide. Also are you curious how G1 GC log looks? Check out Reading & Analyzing G1 GC Logs post.

POV: Since G1 GC has become the default algorithm since Java 9 and it’s consistently enhanced over the years, I will consider using this algorithm especially if my heap size is less than 32gb.

5. Shenandoah GC 

The Shenandoah Garbage Collector is one of the latest and low-pause-time GC algorithms introduced by Red Hat for the OpenJDK. This algorithm is designed to achieve minimal pause times for applications with large heap size. Similar to G1, Shenandoah divides the heap into regions, allowing for targeted collection of memory regions with high garbage content, thus optimizing memory reclamation. This algorithm is supported only from OpenJDK 8u, 11, and later versions.

Do you want to learn about optimizing Shenandoah GC performance, check our Shenandoah GC Tuning guide. Also are you curious how Shenandoah GC log looks? Check out Reading & Analyzing Shenandoah GC Logs post.

POV: If your application has a large heap (say more than >32gb) and you can tolerate high CPU consumption, consider evaluating this algorithm.

6. ZGC

The Z Garbage Collector (ZGC) is a scalable, low-latency garbage collection algorithm. Designed to manage very large heaps with minimal pause times, ZGC aims to keep GC pauses below 10 milliseconds, making it ideal for applications requiring consistent responsiveness and large memory allocations. Initial versions of ZGC had poor performance, but significant improvements have been made in recent versions. The ZGC algorithm was introduced only in Java 11.

Do you want to learn about optimizing ZGC performance, check our ZGC Tuning guide. Also are you curious how ZGC log looks? Check out Reading & Analyzing ZGC Logs post.

POV: If your application is running on a large heap and running on the latest version of Java (say > Java 21), you can consider evaluating this algorithm. However, you need to be aware of allocation stalls.

7. Epsilon GC

The Epsilon is basically a no-op GC algorithm, i.e. it will not reclaim any objects from the memory. Basically, when the heap gets full, the application will crash with an OutOfMemoryError. By not performing any GC operations, Epsilon minimizes runtime overhead associated with garbage collection. This can be useful for performance testing to isolate the impact of GC on application performance. Epsilon GC was introduced in Java 11.

POV: You can use Epsilon GC, in Performance labs to study the impact of GC pauses or for experimentation purposes but not in a production environment.

GC algorithms Comparison Summary

Below table summarizes the key details for each GC algorithm discussed in the previous section:

GC AlgorithmWhen to use it?Supported Java VersionsHow to enable it?
Serial GCNot recommended because of single threaded natureJava 1.2 and above-XX:+UseSerialGC
Parallel GCRecommended if you want to achieve high GC throughput and OK with consistent GC pauses are regular intervalsJava 5 and above-XX:+UseParallelGC 
CMS GCNot recommended, as it’s removed from Java 14 version.Java 1.4.2 to 13-XX:+UseConcMarkSweepGC
G1 GCRecommended if heap size is <32gb and want to strike balance between throughput and pause time.Java 7 and above-XX:+UseG1GC
Shenandoah GCRecommended for large heap (say more than >32gb) and can tolerate high CPU consumptionOpenJDK 8u, 11 and above-XX:+UseShenandoahGC
ZGCRecommended for large heap (say more than >32gb) and running on latest version of Java (say > java 21). Beware of allocation stalls.Java 11 and above-XX:+UseZGC
Epsilon GCCan be used for experimentation purpose and in Performance Labs, but not in productionJava11 and above-XX:+UseEpsilonGC

Choosing Ideal GC Algorithm

Despite the above given details, choosing the ideal Garbage Collection algorithm from these 7 options can be tricky. Thus, to help you further in the selection process, we’ve developed a ‘Choosing Ideal GC Algorithm Flowchart’  below. This Flowchart guides you in selecting the best GC algorithm based on your performance goals and heap size requirements. This flowchart helps you pick the best GC algorithm by considering key factors like latency, throughput, and memory size to guide your decision. 

Fig: Flow chart to help you to arrive at right GC algorithm

Evaluate GC Performance 

Before switching to a different GC algorithm, it’s essential to conduct thorough performance testing to ensure that the application’s performance characteristics improve. Here is a post which guides you how to do GC analysis in 3 simple steps.

Conclusion

Hopefully the pointers given this post, will facilitate you to choose right life partner (i.e. Garbage Collector 😊) for your application. I would like to conclude this post with good news: It’s not quite easy to change the actual life partner in real life, however it’s not the case in changing the GC algorithm. It’s just a matter of making a minor JVM setting change. Even if you make a mistake in choosing the wrong GC algorithm, it’s easy to correct it 🙂

Quick Visual Recap

Here’s a concise infographic from our LinkedIn post that highlights all the 7 GC Algorithms!