JEP 376: ZGC – Concurrent Thread-Stack Processing
Almost there – Garbage collection with the Z Garbage Collector is almost completely free of infamous safepoint operations. Stack processing is the only thing not really running in a concurrent phase yet. JEP 376 addresses exactly this problem and should finally put an end to unnecessary pauses. Let’s take a closer look.
A new Java Enhancement Proposal (JEP), aims to make some changes to ZGC’s stack processing. In the past, garbage collection has caused pauses and scalability issues in HotSpot, which is why there has been a shift away from using safepoints. These safepoints cause a brief but noticeable pause (that can add up significantly on larger machines) when running a program, because the heap is being checked to ensure it is consistent. As a result, everything stands still for a moment before garbage collection can begin – this is of course undesirable.
The ultimate goal for a future version of Java is that garbage collection should run completely concurrently with the program flow, causing minimal to no disruption. This means getting rid of moments where the GC pauses, such as at safepoints. Thus far, improvements have already been made resulting in great progress in terms of reducing the pause times. Marking, relocation, reference processing, class unloading and root processing have been removed, and the only thing left to tune up before ZGC can run concurrently is stack processing. That’s where JEP 376 comes in.
The main aim of JEP 376 is to move thread-stack processing from ZGC safepoints. Additionally, it aims to make stack processing lazy, cooperative, concurrent, and incremental, as well as to remove all other per-thread root processing from ZGC safepoints. The JEP also states that the goal is NOT to implement concurrent per-thread processing of non-GC safepoint operations, such as class redefinition.
JEP owner Erik Österlund writes:
The ZGC Garbage Collector (GC) aims to make GC pauses and scalability issues in HotSpot a thing of the past. We have, so far, moved all GC operations that scale with the size of the heap and the size of metaspace out of safepoint operations and into concurrent phases. Those include marking, relocation, reference processing, class unloading, and most root processing.
The only activities still done in GC safepoints are a subset of root processing and a time-bounded marking termination operation. The roots include Java thread stacks and various other thread roots. These roots are problematic, since they scale with the number of threads. With many threads on large machine, root processing becomes a problem.
In order to move beyond what we have today, and to meet the expectation that time spent inside of GC safepoints does not exceed one millisecond, even on large machines, we must move this per-thread processing, including stack scanning, out to a concurrent phase.
Once the work of JEP 376 is implemented, nothing of importance will be done inside ZGC safepoint operations. Furthermore, this work will potentially provide a platform for other projects to unify lazy stack processing. Projects Loom and JFR could benefit, for example.