The Garbage Collector in Java

Garbage collection in Java - image of garbage bags and bin

We all take the JVM garbage collector for granted, but how many of us really understand what it does, how it does it, and whether we can tune it in any way to perform optimally for our applications?

Garbage Collector Responsibilities

We talk about the garbage collector as if it just collects garbage (whatever that is supposed to mean). It does more than just that. It is responsible for the memory management for the JVM. This includes the following tasks:

  • Decides how heap memory allocation is done.
  • Decides on the memory spaces and layouts.
  • Decides on size and layout of objects.
  • Allocates objects in memory.
  • Tracks objects over their lifetimes.
  • Collects garbage, i.e. clears any unused objects from memory.
  • Reallocates space and memory as required for optimisation (e.g. compacts fragmented memory).
  • Adds in JIT compilation code, e.g. new addresses of moved objects.

So choosing the correct garbage collector can affect almost everything in the JVM! Selecting the right one for the task is crucial in achieving better application performance and stability.

A lot of garbage collectors have two generations: young and old. A quick, less accurate collector is used in the young generation. This may leave some garbage in memory. Because the collector is quick, we should try have objects collected in the young generation, i.e. short-lived objects (true for programming too). Objects got promoted to the old generation when they have survived one or a number of GC cycles. They are said to be tenured at this point.

Because memory is so important to the garbage collector, one of the primary tuning options is the -Xmx flag. This sets the maximum heap size. This gives us the biggest bang for our buck! Usually more memory is better for low pause GCs.

The rule of thumb is to start with -Xmx set at twice the size of the live set. The live set is the stable size of the heap after garbage collections while running the application.

Garbage Collection Algorithms

There are two JVMs available with the OpenJDK: the Hotspot JVM from Oracle and the OpenJ9 JVM from IBM.

Between them they have 12 different garbage collectors, more than 120 tuning flags, multiple memory spaces and pools, and a huge number of GC logging options.

The Hotspot JVM has seven garbage collectors, while the OpenJ9 JVM has six. One of the garbage collectors, Epsilon, is the same for both JVMs. 

Not all of these garbage collectors are available in all versions of Java. As the American car adverts say, “Your mileage may vary.”

We can explicitly specify the GC algorithm using the command line options, or we can use the default GC algorithm set by the Java version. Java 8 uses the Parallel GC as the default GC. Java 9 uses the G1 GC as the default. The default settings will often not be optimal for our applications, but they’re generally “good enough” to run with at first. And definitely a good place to start doing optimization.

It’s always worthwhile trying out a different GC algorithm if our application isn’t meeting its performance targets. The cause of this is mostly from the GC.

We will not be discussing all the GC algorithms now, but it is important to know the command line options to choose a particular GC:We will not be discussing all the GC algorithms now, but it is important to know the command line options to choose a particular GC:

Garbage Collector HotSpot Parameter
Epsilon -XX:+UseEpsilonGC
Serial -XX:+UseSerialGC
Parallel -XX:+UseParallelGC / -XX:+UseParallelOldGC
CMS -XX:+UseConcMarkSweepGC / -XX:+UseParNewGC
G1 -XX:+UseG1GC
Shenandoah -XX:+UseShenandoahGC
ZGC -XX:+UseZGC
Garbage Collector OpenJ9 Parameter
Epsilon -Xgcpolicy:nogc
Throughput -Xgcpolicy:optthruput
Balanced -Xgcpolicy:balanced
Generational -Xgcpolicy:gencon
Metronome -Xgcpolicy:metronome
Pause Optimized -Xgcpolicy:optavgpause
Garbage Collector HotSpot OpenJ9 Description
Epsilon -XX:+UseEpsilonGC -Xgcpolicy:nogc terminate, no GC
Serial -XX:+UseSerialGC targeted at 1 CPU
Parallel -XX:+UseParallelGC throughput
Throughput -Xgcpolicy:optthruput throughput
CMS -XX:+UseConcMarkSweepGC pause time
G1 -XX:+UseG1GC pause time
Shenandoah -XX:+UseShenandoahGC pause time
ZGC -XX:+UseZGC pause time
Balanced -Xgcpolicy:balanced pause time
Generational -Xgcpolicy:gencon pause time
Metronome -Xgcpolicy:metronome pause time
Pause Optimized -Xgcpolicy:optavgpause pause time

For more details see (from https://xperti.io/blogs/java-vm-options-guide/)

Garbage collection algorithms

We’ll describe just a few of the garbage collection algorithms here:

  •   Serial Garbage Collector is the simplest GC implementation. It uses a single thread to perform the garbage collection. It pauses the running application threads until garbage collection is done. It increases the application pause time and decreases the application throughput. Not good for multi-threaded applications.
  • Parallel Garbage Collector is also known as the Throughput GC. Similar to the Serial GC, it also pauses the running threads while doing the garbage collection, but uses multiple GC threads instead of a single thread. It is the default garbage collector in Java 8.
  • CMS Garbage Collector is known as the concurrent mark-sweep GC. It uses multiple threads to scan the heap memory continuously to mark objects that are unused and then sweep the marked objects away. The CMS GC provides better application throughput, but uses more CPU cycles than the parallel collector. 
  • G1 Garbage Collector was designed as a server-style garbage collector for multiprocessor machines with a large amount of RAM. The G1 collector is recommended for applications requiring large heaps (~6 GB and over) and short GC pause times below 0.5 seconds. G1 also compacts the free heap space after garbage collection. It is the default garbage collector of Java 9. 
  • Epsilon GC eliminates garbage collection entirely. It is also known as a “no-op” garbage collector. It handles memory allocation, but doesn’t actually reclaim any memory. When the available heap is full, the JVM terminates with an `OutOfMemoryError`. There are some cases when we know that the available heap will be enough, so we don’t want the JVM to use any resources to run GC tasks. Otherwise we generally only use it for testing. It was introduced in Java 11. 

Ending Off

Look out for more interesting Java facts and tips in next week’s post. And don’t forget to share your comments and Java experiences.

And if you found this useful, subscribe to my weekly Java tips!

Leave a Comment

Your email address will not be published. Required fields are marked *

Code like a Java Guru!

Thank You

We're Excited!

Thank you for completing the form. We're excited that you have chosen to contact us about training. We will process the information as soon as we can, and we will do our best to contact you within 1 working day. (Please note that our offices are closed over weekends and public holidays.)

Don't Worry

Our privacy policy ensures your data is safe: Incus Data does not sell or otherwise distribute email addresses. We will not divulge your personal information to anyone unless specifically authorised by you.

If you need any further information, please contact us on tel: (27) 12-666-2020 or email info@incusdata.com

How can we help you?

Let us contact you about your training requirements. Just fill in a few details, and we’ll get right back to you.

Your Java tip is on its way!

Check that incusdata.com is an approved sender, so that your Java tips don’t land up in the spam folder.

Our privacy policy means your data is safe. You can unsubscribe from these tips at any time.