This month we are focusing on Java 12 new features you should know about. In other articles, we looked at Java 12’s JVM Constants API explained in 5 minutes, Java 12 made microbenchmarking easier and Java 12 Switch Expressions explained in 5 minutes
In this article, we look at the new Garbage collection options.
While Java 12 is not a long-term release, it does see some very significant enhancements. Garbage collection has always been one of the killer features of Java over other languages such as C, and there are big changes here. So what is new?
1. Shenendoah: A new Garbage Collector
The default Garbage Collector in Java 12 will still be G1, but it also comes with a new experimental GC called Shenendoah.
Shenendoah is a concurrent and parallel open source Garbage Collector. The purpose is to provide good responsiveness and short, reliable pause times. The trade off is that it prioritises responsiveness over throughput and memory footprint. It requires more space than other algorithms. It is not designed to be the best for every scenario, but if you want responsiveness and a GC that takes advantage of modern machines with more memory and more processors, it is an interesting option.
How does it compare to other GCs?
In Java 8, the default GC was the Parallel Garbage Collector, which prioritized high throughput. It used multiple threads for GC, but froze all other application threads while doing so (an approach known as ‘stop the world’ which led to high pause times).
Since Java 9, the default GC has been Garbage First (G1). Unlike Parallel GC, it only uses ‘stop the world’ when doing a full garbage collection. This algorithm provides shorter pause times. You can read much more detail about G1GC here.
Like G1, Shenendoah can work alongside the application threads. However it also uses concurrent evacuation, which G1 does not. It can do evacuation work and compact the heap while Java threads are running, reducing pause times even further. The pause time is independent of the heap size (a large heap would have the same pause time as a smaller heap).
How does Shenendoah work?
Normally OpenJDK object headers have 2 words allocated to them (the class name and a mark word used for locking, forward pointers etc). Shenendoah adds a third word called an Indirection Pointer. All references to the object have to go through this pointer. This allows the object to be moved without updating all references to it, meaning live objects can be updated while Java threads are running concurrently. This extra word is only added when the Shenendoah GC is in use.
- Initial Marking (short stop the world phase, traces the root set)
- Concurrent Marking (longer concurrent phase)
- Final Marking (short stop the world phase)
- Concurrent Compaction (longer concurrent phase)
More detail about how it works can be found in this paper.
How can I try Shenendoah?
If you are using Java 12, you can use Shenendoah by using the following VM flags:-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
(The first flag is needed because it is experimental.)
2. Enhancements to G1GC
There are also 2 noticeable enhancements to G1GC (which since Java 9 has been the default Garbage Collector).
G1 starts by choosing how much work needs to be done in a garbage collection (this is called the collection set), and then starting the collection. After the collection has started, it has to collect every live object without stopping. The drawback is that this can take a long time if the collection set is too big.
JEP-344 provides an enhancement to the algorithm. If it keeps choosing the wrong number of collections and taking too long, it splits it into 2 parts. One part has to be done, the other part becomes optional. The optional part can be done if the collection hasn’t gone over the pause time target. It can then stop after completing any part of the optional phase.
A second drawback is that G1 would only return heap memory to the operating system when the GC was full, or when running concurrently. Neither of these things happen very often (G1 actively avoids getting full).
JEP 346 solves this issue by making the GC try to trigger a concurrent cycle during periods of inactivity. When this happens, heap memory will automatically be returned to the Operating System.
So Java 12 has made some significant improvements to the default GC, and added a new optional algorithm which you can try out if you like. Have you tried it yet? What are your thoughts?