JEP 189: Shenandoah: An Ultra-Low-Pause-Time Garbage Collector
|Author||Christine H. Flood, Roman Kennke|
|Discussion||hotspot dash gc dash dev at openjdk dot java dot net|
Reduce GC pause times on extremely large heaps by doing evacuation work concurrently with Java threads and making pause times independent of heap size.
This is not the one GC to rule them all. If you are running with a heap of 20GB or less or if you are running with fewer than eight cores, you are probably better off with one of the current GC algorithms.
This project will be a success if we can manage 100GB+ heaps with < 10ms pause times. This is somewhat misleading because our pause times are proportional to the size of the root set, not the size of the heap, however it is a useful metric to describe how the GC will affect applications. We are aiming for a performance penalty of no more than 10% over the current collectors on applications with no GC pauses and no perceivable performance penalty when running any of the current collectors.
Users that are running with 100GB heaps are not able to tolerate the pause times of the current garbage collectors.
Shenandoah is a region-based garbage collector with a heap structure similar to G1. Garbage collection in Shenandoah is performed in phases. The first phase is a marking phase, the second is an evacuation phase. Both activities are carried out concurrently with mutator threads, using several parallel GC threads. During the marking phase, all live objects in the heap are marked, starting from the GC roots (thread stacks, etc). A count of live data in each region is maintained. In the evacuation phase, the best regions to collect are identified and the live objects in those regions are copied to new regions. During the next concurrent marking phase all references are updated to point to the evacuated objects, and at the end of this phase the evacuated regions may be reclaimed.
The key to performing concurrent evacuation is having the Java Threads and the GC threads agree on the location of objects. This is accomplished in Shenandoah by the use of a Brooks forwarding pointer. All reads by the Java Threads indirect through this forwarding pointer. All writes to objects in targeted regions must first copy the object and then write to the object in its new location.
One advantage of this approach is that it is no longer necessary to maintain remembered sets. This collector will focus GC effort on the regions with the most garbage regardless of their age, as opposed to G1 which focuses on young generation collection to help limit remembered-set size.
Zing/Azul has a pauseless collector, however that work has not been contributed to OpenJDK.
G1 does some parallel and concurrent work, but it does not do concurrent evacuation.
Normal testing should flush out correctness issues. Testing must be done on a large machine with multiple processors and 100GB or more of memory. Because this is a fundamental VM change, it will require extensive testing. If this feature is included in a JDK release then it will present an additional testing burden for those wishing to support it.
Risks and Assumptions
There will be several changes in Hotspot that could affect behavior even when running without the Shenandoah GC. In particular, we will need to add read and write barriers into various places. For runtime-compiled code (C1, C2, interpreter) this will most likely not be an issue, as compilation can be done conditionally. However, there are also several places in runtime code that require barriers. When running without Shenandoah, those will call no-op/empty functions. Our goal is to have this overhead be undetectable.