RFR: Traversal: Don't traverse new objects

Roman Kennke rkennke at redhat.com
Wed Feb 21 11:24:45 UTC 2018


Hi Aleksey,

On Tue, Feb 20, 2018 at 12:22 PM, Aleksey Shipilev <shade at redhat.com> wrote:
> On 02/19/2018 06:48 PM, Roman Kennke wrote:
>> Full-patch:
>> http://cr.openjdk.java.net/~rkennke/traversal-no-traverse-new/webrev.01/
>
> *) I still do not get the interaction with next TAMS, can you explain a bit? Still very weird to see
> TAMS modified *during the cycle*. It might deserve the comment.

The idea behind this patch is to avoid traversing through new objects because:
- We will not reclaim them in this cycle anyway, because they are not
in the cset
- It makes up for the bulk of work during final-pause (with this
patch, goes down from 70ms -> 4ms)
- It also shortens the concurrent cycle because we don't need to
pointlessly traverse through newly allocated objects.
- As a nice side-effect, it solves the I-U termination problem
(mutators cannot outrun the GC by allocating like crazy)
- It is an easy way to achieve MWF. What MWF does is to also enqueue
the target object of stores if it's new. Treating new objects live
implicitely achieves the same, but without extra barriers. I think the
effect of shortened final-pause (mentioned above) is the main
advantage of MWF. In particular, we will not see the head of a
completely new long linked list in final-pause and end up traversing
huge chunks of the heap there.
- We don't need to see/update the fields of new objects either,
because they are either still null, or anything that's been stored
into them has been evacuated+enqueued before (and will thus be treated
later).

We achieve this by setting TAMS for each region, and everything
allocated beyond TAMS will be 'implicitely marked'.

Gotchas:
- While we want new objects to be implicitely marked, we don't want to
count them alive. Otherwise the next cycle wouldn't pick them up and
consider them for cset. This means that we need to protect such
regions from getting accidentally thrashed at the end of traversal
cycle. This is why I keep track of alloc-regions and check
is_alloc_region() in the trashing code.
- We *need* to traverse through evacuated objects. Those objects are
pre-existing, and any references in them point to interesting objects
that we need to see. We also want to count them as live, because we
just determined that they are alive :-) I achieve this by upping TAMS
concurrently for every gclab/gc-shared alloc before publishing the
evacuated object. This way, the GC threads will not consider such
objects implictely marked, and traverse through them as normal.

This change both improves throughput and pause time considerably.

Does the explanation make things clearer?

Roman


More information about the shenandoah-dev mailing list