RFR: Traveral GC heuristics
Roman Kennke
rkennke at redhat.com
Tue Jan 16 18:38:42 UTC 2018
This started out as a smallish partial-GC experiment, then into a clone
of partial GC, and ended up as a standalone GC mode for Shenandoah,
which is a frankensteinization of partial+concurrent-marking, with some
goodies :-)
The idea is to do everything, marking+evacuation+update-refs, in one
single phase. This is not very difficult to do: while traversing,
evacuate objects that are in the Cset, and update references as we go. I
chose to traverse the heap using an incremental-update approach, mostly
because this is what partial GC does, and as said above, this started
out as a clone of partial :-)
The tricky part is to choose the Cset: I made it such that each GC cycle
collects liveness information, and bases the decision about Cset in the
next cycle on that liveness information. Yes, this means the first cycle
does not collect anything (except immediate garbage).
Advantages:
- obviously, touching all live objects only once means less time spent
in GC. Measurements show that traversing the heap and doing everything
is only slightly longer than Shenandoah's marking phase, and this might
actually be because we also need to mark through newly allocated objects.
- Traversal-order evacuation gives us 10x increase in ordering-sensitive
microbenchmark: https://shipilev.net/jvm-anatomy-park/11-moving-gc-locality/
- Simpler barriers: i-u style barriers don't need to load the pre-value,
and can be optimized much better (hoisted out of hot paths, etc). Some
of it is already done in this patch, but there are plenty of
opportunities to make it even better.
- Possibly less floating garbage because we trace through newly
allocated objects too, and don't treat it implicitely live.
- we don't need a keep-alive-barrier for Reference.get() which means we
keep fewer referents alive just because they happen to be accessed
during GC.
- MWF is only a switch away (if I understand MWF correctly):
-XX:+ShenandoahMWF
- It does not need RBs in the WB fast-path, because outside of the
single phase, nothing is ever forwarded.
- It does not need the membar stuff in the WBs because we turn on/off
the phase during safepoint
Disadvantages:
- Store-value barrier needs to be a WB, RB is not sufficient. The
storeval barrier is there to ensure only to-space values ever get
written to fields during update-refs. 3-phase Shenandoah doesn't
evacuate during update-refs, and therefore RB is enough. We need WB
here. (I believe this is off-set by optimization opportunities, see above)
- Known I-U problem: mutators can outrun the GC with allocations and let
us not terminate.
- It needs barriers for constants (need to check this).
Stuff left to do:
- Implement sane degeneration: if we hit OOM, we simply restart and go
into full-GC.
- Depending on degen: make heuristics adaptive. Currently it requires
manual tweaking of thresholds.
Relevant knobs:
- ShenandoahGarbageThreshold: regions with more garbage than this go
into the Cset. Notice that this is based on the *previous* cycle, so we
may actually have much more garbage (but not less).
- ShenandoahFreeThreshold: start GC when we have less than that much
free heap.
I'll not go into all the details for now and give you the code:
http://cr.openjdk.java.net/~rkennke/traversal/webrev.00/
Roman
More information about the shenandoah-dev
mailing list