RFR (XS): 7114095: G1: assert(obj == oopDesc::load_decode_heap_oop(p)) failed: p should still be pointing to obj
John Cuthbertson
john.cuthbertson at oracle.com
Thu Dec 1 18:47:02 UTC 2011
Hi Ramki,
Response inline....
On 12/01/11 01:08, Srinivas Ramakrishna wrote:
> Hi John -- I am wondering if we should simply prevent the second
> closure application if we have already
> done it once. That is easy to set-up. Of course such a test penalizes
> all collectors when they scan Reference objects, but perhaps the cost
> of the test
> will be lost in the noise of all the other test-logic when scanning
> Reference objects...
Yes this is straight forward to do - but as you said it will add an
additional clause to the if-condition that's around the second
application. I have verified that this does seem to fix the problem. If
I go with this approach - do you foresee any issues with skipping the
second application of the closure to the discovered field? I don't think
there is but it's worth asking the original author. :) I went with the
fix that's in the webrev because it only touches G1 code.
> But since G1 deals with multiple scans anyway, perhaps this is not
> worth worrying over.
>
> I also have some related questions: what forces G1 to have to deal
> with the multiple scan possibility anyway?
> I had previously incorrectly assumed that G1's remembered set scanning
> also avoids multiple scans of any
> reference...
During RSet scanning - the closure claims a group of cards. That along
with a couple of "is_in_cset" tests seem to be enough to prevent
multiple scans of the same reference field (i.e. pushing the same
reference field on to the _refs_to_scan queue more than once).
> In the scenario that you outlined, when the second thread processes
> the reference the second
> time, how does it determine that it should do nothing? (I guess I am
> asking for a pointer to the
> code that does the processing of the stack elements and does the
> recursive copying.)
The failing assert indicates that reference fields should only be pushed
on to the _refs_to_scan queue once and mostly that seems to be the case.
After an object is evacuated, the worker that successfully installs the
forwarding pointer iterates over the reference fields of the evacuated
object, applying the G1ParScanClosure. Since this is done by the thread
that claims the object - we shouldn't see multiple pushes of the same
reference field unless the closure is applied to the reference field
multiple times. The discovered field of reference objects is the only
field I've seen being pushed twice.
As reference fields are popped from the _refs_to_scan queue, a copy
closure is applied to them. This takes place in
G1ParScanThreadState::deal_with_reference(). I believe the call in
question comes from G1ParScanThreadState::trim_queue(). The copy closure
dereferences the field and first checks that the referenced object is in
the CSet before attempting to evacuate the object. This code is in
G1ParCopyClosure::do_oop_work() and
G1ParCopyHelper::copy_to_survivor_space() in g1CollectedHeap.cpp.
The evacuation code has to handle multiple reference fields that point
to the same object (an already evacuated object will not pass the
"is_in_cset" check), and it looks like it can handle seeing the same
reference field twice. The worst that I think can happen is that the
reference field may be updated twice (to point to the same evacuated
object).
A while back, the evacuation code took a fourth template parameter - a
boolean that indicated whether the "is_in_cset" check could be skipped,
i.e. it was assumed that the closure would only be applied to objects in
the collection set. If we still had that code then we wouldn't be able
to handle this scenario.
Does this answer your questions?
Thanks,
JohnC
More information about the hotspot-gc-dev
mailing list