RFR: Refactor allocation failure and explicit GC handling

Aleksey Shipilev shade at redhat.com
Tue Jan 16 21:36:58 UTC 2018


http://cr.openjdk.java.net/~shade/shenandoah/refactor-af-explicit-gc/webrev.01/

This refactors the allocation failure and explicit GC handling, and prepares the code for the
arrival of STW Degenerate GC.

Tour of changes:

1. For historical reasons, we used to have the full_gc_* members in ShConcThread to handle the
allocation failure, because that was the only option available for us. With the advent of degenerate
CM and UR it started to mean just the "allocation failure". With Degenerate GC, it would further
depart from its original meaning. So, renaming full_gc_* to alloc_failure_* to capture the real
intent and rewiring accordingly is one part of the refactoring.

Behavioral change: Alloc-failed threads are not immediately kicked after degenerated CM and
degenerate UR, and instead they wait for the end of the cycle. This avoids a bad race against the
alloc-failed threads that are coming with cancellation at the same time, and it keeps us away from
OOM-during-evac when after-CM cleanup cannot regain enough space. This would be the behavior of the
upcoming Degenerated GC anyway.


2. There is also the path that invokes explicit GCs. Again, for historical reasons, that originally
meant only Full GC. With the advent of ExplicitGCInvokesConcurrent support, it means both concurrent
and Full GC cycles! So, renaming conc_gc_* to explicit_gc_* and rewiring accordingly is the second
part of refactoring.

Behavioral change: Explicit GC no longer cancels the concurrent cycle, instead it waits for another
control loop iteration to start explicit GC. This is for the best, because it both simplifies our
handling logic, and allows requesters to wait for their own cycle. This is interesting when
concurrent cycle is running, ExplicitGCInvokesConcurrent is enabled and System.gc() is called: the
requesting thread would wait for one complete GC cycle to start and finish.


3. The logic in main control loop used to handle weird paths from cancellations back to Full GC.
Having proper designations for alloc failure and explicit GCs help to write out the proper
priorities for these events. This also allows us to potentially plug Degenerate GC for the
out-of-cycle Allocation Failures, instead of unconditionally doing the Full GC.


4. Pulling the code out of ShenandoahHeap back to ShenandoahConcurrentThread allows to reduce
coupling. Also, ShenandoahGCCause is eliminated in favor of proper GCCause, which simplifies logic
further.


5. Additionally, gc+stats now tells things like these:

----- 8< ----------------------------------------------------------------------------------------

 Under allocation pressure, concurrent cycles will cancel, and either continue phase under
 stop-the-world pause or result in stop-the-world Full GC. Increase heap size, tune GC heuristics,
 or lower allocation rate to avoid degenerated and Full GC cycles.

   85 successful concurrent GC cycles
   27 cancelled concurrent GC cycles (5 degenerated marks, 10 degenerated update refs, 12 Full GCs)
   11 out-of-cycle allocation failures (11 Full GCs)
    0 explicitly requested GC cycles (0 Full GCs)

----- 8< ----------------------------------------------------------------------------------------


Testing: hotspot_gc_shenandoah

Thanks,
-Aleksey



More information about the shenandoah-dev mailing list