Core Java

Java 12 Garbage Collector Example

The LISP language had the first garbage collector back in 1959. Since then many languages like Java have adopted Garbage collection for automatic memory management. Today we will look at the Shenandoah garbage collector.

1. Introduction

Garbage Collection is Java’s automatic memory management method. Garbage Collection occurs in the Java Heap memory whenever the Java heap memory is full. There are several garbage collectors available for Java. The Shenandoah garbage collector is the Ultra-Low-pause-time Garbage Collector. RedHat developed this Garbage Collector. Some of the OpenJDK Java 12 versions support this garbage collector as an experimental feature. This garbage collector is the default Garbage Collector in the later builds of OpenJDK Java.  

2. What is a heap?

Before we understand how Garbage collection works, we need to understand a little bit about the Java Heap memory. Traditionally, the Java Heap has two main parts or generations

  • Nursery or Young Generation:

    The nursery or the young generation space is reserved for new object allocation. When Java programs run, they allocate new objects like arrays and class instances from this space. The nursery or the young generation has 3 parts:

    • Eden Memory. This space contains most of the newly created objects. If this space fills up, then the JVM can perform minor garbage collection (Minor GC) to clean up space.
    • 2 Survivor Memory spaces. There are two survivor spaces available. After MinorGC, all the “surviving(still active objects or objects with references)” objects shift to survivor spaces. These spaces are generally empty.
  • Old Generation: Objects that survive multiple Minor GC cycles are shifted to the Old generation space. These Objects are garbage collected only after all the heap memory is full. This is the Major GC cycle.

The nursery space is mainly for short-lived, temporary objects that can be retrieved fast.

Java 12 Garbage Collector - Heap Memory
Heap Memory

3. What is Garbage Collection?

Garbage Collection or GC is a tool of memory management. The JVM runs GC in the background as and when required.

3.1 History

  • In Languages like C, memory management was the responsibility of the programmer.
  • Incorrect code led to memory lock-ups i.e. memory leaks.
  • This led to memory leaks and consequently memory issues.
  • To reduce the number of memory leaks, many languages including Java automated this process of memory management. This is Garbage Collection.

3.2 Steps in Garbage Collection

The JVM uses the “Mark and Sweep” Algorithm for garbage collection in Java. As the name suggests there are 2 phases in this algorithm: Mark and Sweep.

  • Mark: In the mark phase of the algorithm, all the “still in use” or still referenced objects i.e. alive objects are identified. All the other objects are garbage.
  • Sweep: all the garbage is collected and freed.

While GC runs, the Java applications need to pause completely i.e. “Stop-The-World” pause.

3.3 Types of Garbage Collectors

Depending on their implementation details and the Application pause they cause, there are 4 main garbage collectors that are present in Java.

  • Serial Garbage Collector: This is the simplest garbage collector. It pauses all the applications running and does the Mark and sweep phases i.e. it utilizes the “Stop-the-world” pause.
  • Parallel Garbage Collector/Throughput collector: This was the default garbage collector till Java 8. This also utilizes the “Stop-the-world” pause. This collector runs using multiple threads and hence the pause tends to be much shorter that the serial garbage collector.
  • CMS Garbage Collector/Concurrent Mark and Sweep: This uses multiple threads and only pauses the application in the sweep phase and not while marking. This significantly reduces the stop-the-world pause time. However, it still exists.
  • G1 Garbage Collector / Garbage First Collector:
    • This is the default garbage collector since Java 9 and the default in Java 12 also.
    • It is much more efficient that all the other collectors so far.
    • The G1 GC forgoes the concept of Young and Old generation spaces and breaks the heap space into smaller equal-sized chunks.
    • G1 then marks the garbage objects in each of the chunks. It considers only the chunks with more garbage objects.
    • It also compacts the heap space after garbage collection.
    • This makes this GC one of the most efficient GCs so far. This is very useful for big heap spaces i.e. 4GB or more.
    • However, the pause time still exists

4. Enter: The Shenandoah Garbage Collector

The Shenandoah garbage collector is the Ultra-Low-pause garbage collector. It is an experimental feature in Java 12 through JEP 189. The research paper related to the Shenandoah is available here.

4.1 Working in brief

  • The Shenandoah garbage collector targets large multi-core machines with large heaps.
  • The goal of the Shenandoah garbage collector is to be able to handle heap sizes of 100GB or more and still have extremely low pause times.
  • Just like G1, it forgoes the concept of old and new generation space and divides the heap into regions.
  • It then scans the root set and prepares the application for concurrent marking. This does pause the application briefly, but the pause time is extremely low.
  • After this, the GC traverses the heap and marks all the still-alive or in-use objects while the application is still running. This phase, known as the concurrent mark, does not pause the application and the application is free to allocate new memory during this phase.
  • GC rescans the heap and marks the remaining garbage objects. This phase pauses application execution briefly.
  • Then the active objects are collected and assigned to other available regions of the heap while the application is still running.
  • The GC then frees the garbage objects and also updated the references of the moved active objects. It also compacts the heap memory to reduce fragmentation.

Shenandoah GC does all the activities concurrently and the application pauses very rarely and drastically reduce pause-times. This is the main advantage that the Shenandoah collector has over the G1 garbage collector. To understand the Shenandoah garbage collector in detail, visit the Shenandoah page from OpenJDK.

Java 12 Garbage Collector - Phases of the Shenandoah Garbage Collector
Phases of the Shenandoah Garbage Collector

4.2 How do we use it?

The Shenandoah garbage collector is an experimental feature in the OpenJDK Java 12. Oracle does not support this experimental garbage collector. The Oracle builds support the ZGC garbage collector instead

It’s worth noting that Shenandoah isn’t enabled in the JDK builds that Oracle ships, but other OpenJDK distributors enable Shenandoah by default.  Shenandoah isn’t the only option when it comes to concurrent GCs. ZGC is another GC that is shipped with OpenJDK (including with Oracle’s builds), and it has been improved in JDK 12.

Java Magazine , December 2019 issue

To enable the Shenandoah garbage collector in JDK 12 we need to use the VM flags

-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC

5. Example

Let’s look at an example to see the various steps of the Shenandoah garbage collector when invoked forcibly. To be able to use the Shenandoah Garbage Collector, you need one of the Jdk distributions that support it. For example: AdoptOpenJDK, Amazon Corretto etc.  I have the AdoptOpenJDK 12 (jdk-12.0.2.10-hotspot) installed on my system. Not all distributions of Java 12 support it. Details of which builds do and don’t support Shenandoah are mentioned in the wiki page.

Java 12 Garbage Collector - Shenandoah GC not supported
Shenandoah GC not supported

To see the Shenandoah GC in action, we will consider a small program for a Car Inventory system. This system has some incorrect entries of old scrap cars (old car objects 1 and 2). We have added code remove those cars from our inventory. We are going to force GC to be called by using the System.gc() method in the MainClass.java. Also, we run the finalize() method in the Car.java. The source code is as follows:

Car.java

class Car 
{ 
    private int ID; 
    private String name;
    private String year;	
    //Get rhe effect of global variable
	private static int nextId=1; 
    
    public Car(String name,String year) 
    { 
        this.name = name; 
		this.year = year;
        this.ID = nextId++; 
    } 
    public void show() 
    { 
        System.out.println 
        ("Id="+ ID +"\nName="+ name +"\n Year" + year); 
    } 
    public void showNextId() 
    { 
        System.out.println 
        ("Next car id will be="+nextId); 
    } 
    // Decrementing the Id so that the old scrap cars are not counted when their Objects are garbage collected.
    protected void finalize() 
    { 
        --nextId;  
    } 
} 

MainClass.java

public class MainClass 
{ 
    public static void main(String []args) 
    { 
        Car c1 = new Car("Toyota Camry","2018"); 
        Car c2 = new Car("Acura ","2017");  
        Car c3 = new Car("Toyota RAV4","2020"); 
        //Just output
        c1.show(); 
        c2.show(); 
        c3.show(); 
        c1.showNextId(); 
        c2.showNextId(); 
        c3.showNextId(); 
            
        {  
            //It is sub block to add some super-old cars which we do not want to count in the inventory.
            Car old1 = new Car("Toyota Camry","2000");     
            Car old2 = new Car("Fiat","1988");
            old1.show(); 
            old2.show(); 
            
	    old1.showNextId(); 
            //This should show incremented ID as 6.
            old2.showNextId(); 
            //Make objects eligible for garbage collection
	    old1 = old2 = null; 
            //Force GC 
	    System.gc();  
            System.runFinalization(); 
        } 
    //After GC the Id should be 2 counts less i.e 4.Just to check that 
    //finalize() ran
	c1.showNextId(); 
    } 
} 

Since this is an experimental feature in Java 12 we have to use the following commands to compile and run the application.

Compile:

javac MainClass.java

To enable the Shenandoah Garbage Collector and display the gc logs on the console itself

java -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -verbose:gc MainClass

Output of the Shenandoah Garbage Collector.
Output of the Shenandoah Garbage Collector.

To run without Shenandoah i.e. with the default G1 Garbage collector enabled and display the gc logs on the console itself

java -verbose:gc MainClass
G1 garbage Collection output
G1 garbage Collection output

8. Download the Source Code

Download
You can download the full source code of this example here: Java 12 Garbage Collector Example

Reshma Sathe

I am a recent Master of Computer Science degree graduate from the University Of Illinois at Urbana-Champaign.I have previously worked as a Software Engineer with projects ranging from production support to programming and software engineering.I am currently working on self-driven projects in Java, Python and Angular and also exploring other frontend and backend technologies.
Subscribe
Notify of
guest

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

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Ross Hunt
4 years ago

I must say it’s been written by an expert. The steps suggested inside are very helpful and me, being a non-tech person, can also perform them.

Yatin
4 years ago

Nice written.

Back to top button