Choosing the Right Garbage Collector for Your Java Application

Jill ThornhillJill Thornhill
4 min read

Why does Java have so many different GC algorithms? How do you decide which is best for your application? This article looks at the available Java garbage collection algorithms, and discusses their strengths and weaknesses.

How Does Java Garbage Collection Work?

What does GC actually do?

Java runtime memory is split into various areas, including:

  • Heap: Stores objects;

  • Metaspace: Stores class and other information;

  • Stack: Stores thread information.

The heap is divided into:

  • Young Generation (YG): newly-created objects;

  • Old Generation (OG): objects that have survived cycles of GC.

YG is generally small – often 100-500MB. It’s cleaned frequently in an event known as Minor GC. OG is larger, and with the metaspace, is cleaned infrequently in a Major GC event.

Objects that are no longer referenced are marked by the GC and swept from memory. The GC then compacts the memory. Some of these tasks can take place concurrently, but others must pause all running threads in a stop-the-world event.

For more information, see this article on Java Garbage Collection.

Key Performance Indicators (KPI)

GC efficiency is generally evaluated on:

  • Throughput: Percentage of time spent on application tasks as opposed to GC tasks;

  • Latency: Duration of stop-the-world pauses. Maximum and average latency are important.

  • Footprint: CPU cycles and memory used by GC.

Factors to Consider When Choosing an Algorithm

Before choosing the right algorithm, you need to answer a few questions about your application.

  • How much memory does it require?

  • How important is low latency? Batch processes tolerate fairly high latency. Real-time applications such as robotics and process control need very low latency.

  • How important is high throughput? Applications that process large volumes of data prioritize throughput over latency.

  • How many CPU cores does the hardware have? How much RAM?

  • What version of Java are you running?

Java Garbage Collection Algorithms

The algorithms available in Java are:

  • Serial: The only algorithm available in versions earlier than Java 4. It uses single-threading, has high latency and is rather slow. You may still choose Serial if the application has low memory requirements, or if you’re using a single-core processor. It has a low footprint, and is suitable for embedded systems.

  • Parallel: Also an older algorithm. It has multithreading. Parallel GC has a high throughput but also high latency. It’s suitable for large batch jobs.

  • CMS (Concurrent Mark Sweep): Experimentally released in Java 4 to reduce latency. Much of the work is done in parallel with other threads. It was deprecated in Java 9 and later discontinued, because it didn’t deal well with compaction. It’s no longer recommended.

  • G1: Released in Java 8, it has a good balance between throughput and latency. It’s the default in most modern versions of Java, and works well for most applications. It splits the heap spaces into equal-sized areas for GC purposes, resulting in fast cycles. It also prioritizes areas with a high percentage of garbage.

  • Z: Released in Java 11, its objective was to improve on G1 to further reduce latency. The early releases were a bit unstable, but later releases work well. You’d choose this over G1 if you have a very large heap size – larger than 32GB. Z is heavy on CPU usage, so may not be a good choice if your CPU cores are limited.

  • Shenandoah: Developed by Red Hat as part of OpenJDK, with much the same objectives as Z. It was included in Oracle Java from Java 14. It’s also a good choice for applications with large heap sizes.

  • Epsilon: Used for benchmarking only.

Evaluating the Algorithm

You should always use GC logs to monitor any changes you make to GC, keeping track of KPI.

A great tool for this is GCeasy, which analyzes large logs within minutes. It creates graphs and charts containing KPI and information about how often GC is running, and how much memory is being released. It also gives performance tuning recommendations.

Conclusion

Usually, the default algorithm works well. For small devices, you may choose serial GC, whereas for very large heap sizes, Z or Shenandoah are good choices. Parallel works well for large batch jobs. In all other cases, G1 is usually your best choice.

0
Subscribe to my newsletter

Read articles from Jill Thornhill directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jill Thornhill
Jill Thornhill