Notes on JVM Garbage Collectors

Good morning! Woke up this morning to an outside temperature of 28F. In about a week from now the morning temperatures will be a few degrees lower. This is normal in the Twin Cities of Minneapolis and St. Paul. There is always hope. In about six months the temperature will be warm enough to go outdoors in comfort. In the meantime there are always early weekend walks in the Mall of America.

Earlier today my wife and I had dentist appointments. We thought all was set so at 08:30 AM we drove to the dentist office. When we arrived we sent a text as instructed. The receptionist highlighted some issues that we were under the impression that they all had been taken care of. One thing and the other and we cancelled the appointments on the spot and drove back home. We know the dentist outside his practice. My wife will be contacting him to clear the misunderstanding.

A day or two ago I was chatting with a software engineer about what to do in cases when your server (or microservice) is experiencing performance issues due to the garbage collector (GB) in Java.

My opinion is that you can change the GB and select another from the standard libraries. That is a given and we will look at this in a few. A different thing is to replace the standard GBs with a custom one. If you have to do so, you must have some very specific issues.

In general you could modify your application to reduce the number of objects that need to be created and then released by the GC. If the issue is with buffers, then perhaps one can use some set of fixed sizes and keep them around in order to eliminate (or greatly reduce) the creation / release by the GB operations. This is not specific to Java but the same recommendations would apply to other programming languages, including C / C++, notwithstanding that in C, memory is not automatically managed.

As usual I like to refresh my knowledge in the subject because things change with new software releases. I always start by reading and then experimenting. I apologize in advance that I will not write code to experiment with the different GC in Java. If you are interested you could write some multithreaded code and then invoke it with different commands which will follow some basic descriptions of the standard Java GCs.

I collected information for the GCs from the following articles and documents:

o JVM Garbage Collectors by: Baeldung.

o Improve Application Performance with These Advanced GC Techniques by: Brandon Groves

o Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning by: Oracle

o Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide by: Oracle

While reading the different documents I collected information by copying, pasting and editing information from them. I strongly recommend that if you have a need to replace or write your own GB in Java, to first read the references I used.

Application performance is on the forefront of our minds, and Garbage Collection (GC) optimization is a good place to make small, but meaningful advancements.

To improve application performance, choosing the right GC isn’t enough. 

We need to know how the process works,  and we need to optimize our code so that our GCs don’t pull excessive resources or cause excessive pauses in our application.

The benefit to dividing Object memories based on age is that the GC can operate at different levels.

One quick trick for optimizing GC operation is to adjust the sizes of heap areas to best fit your applications’ needs.

Application performance is directly impacted by the frequency and duration of garbage collections, meaning that optimization of the GC process is done by reducing those metrics. 

There are two major ways to do this. 

First, by adjusting the heap sizes of young and old generations, and second, to reduce the rate of object allocation and promotion.

Garbage Collection in Java works in two simple steps known as Mark and Sweep:

o Mark – it is where the garbage collector identifies which pieces of memory are in use and which are not

o Sweep – this step removes objects identified during the “mark” phase

o Copy/Compaction – this step copies / compacts objects that remain in the heap

VM has five types of GC implementations:

o Serial Garbage Collector

    This is the simplest GC implementation, as it basically works with a single thread. 

    As a result, this GC implementation freezes all application threads when it runs.

    If using this GC, we can specify:

    maximum garbage collection threads and pause time, throughput, and footprint (heap size).

    $ java -XX:+UseSerialGC -jar

o Parallel Garbage Collector

    It’s the default GC of the JVM and sometimes called Throughput Collectors. 

    Unlike Serial Garbage Collector, this uses multiple threads for managing heap space.

    But it also freezes other application threads while performing GC.

    $ java -XX:+UseParallelGC -jar

Low Pause Collectors:

o CMS Garbage Collector

    The Concurrent Mark Sweep (CMS) implementation uses multiple garbage collector threads for garbage collection.

    It’s designed for applications that prefer shorter garbage collection pauses, 

    and that can afford to share processor resources with the garbage collector while the application is running.

    Simply put, applications using this type of GC respond slower on average 

    but do not stop responding to perform garbage collection.

    This GC has been removed starting with Java 14.

    $ java -XX:+UseParNewGC -jar

o G1 Garbage Collector

    G1 (Garbage First) Garbage Collector is designed for applications running on 

    multi-processor machines with large memory space.

    After the mark phase is completed, G1 knows which regions are mostly empty.

    It collects in these areas first, 

   which usually yields a significant amount of free space (i.e. phase 2 known as Sweeping).

    It is why this method of garbage collection is called Garbage-First.

    $ java -XX:+UseG1GC -jar

o Z Garbage Collector

    ZGC (Z Garbage Collector) is a scalable low-latency garbage collector 

    which debuted in Java 11 as an experimental option for Linux.

    JDK 14 introduced ZGC under the Windows and macOS operating systems. 

    ZGC has obtained the production status from Java 15 onwards.

    ZGC performs all expensive work concurrently, 

    without stopping the execution of application threads for more than 10 ms, 

    which makes it suitable for applications that require low latency. 

    It uses load barriers with colored pointers to perform concurrent operations 

    when the threads are running and they are used to keep track of heap usage.

    Reference coloring (colored pointers) is the core concept of ZGC. 

    It means that ZGC uses some bits (metadata bits) of reference to mark the state of the object. 

    It also handles heaps ranging from 8MB to 16TB in size. 

    Furthermore, pause times do not increase with the heap, live-set, or root-set size.

    $ java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC

    or from Java 15

    $ java -XX:+UseZGC

If you have comments or questions regarding this, or any other post in this blog, please do not hesitate and leave me a note below. I will reply as soon as possible.

Keep on reading and experimenting. It is one of the best ways to learn, become proficient, refresh your knowledge and enhance your developer / engineering  toolset.

Thanks for reading this post, feel free to connect with me John Canessa at LinkedIn.



Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.