[External] : Re: [jdk19] RFR: 8290250: Shenandoah: disable Loom for iu mode
Roman Kennke
rkennke at redhat.com
Fri Jul 15 13:52:56 UTC 2022
Hi Erik,
> Hi Roman,
>
> We seem to agree that eliding barriers purely on the basis that the containing object of a field is newly allocated, is invalid. But you seem to say that you do not elide barriers on e.g. initializing stores. If that is the case, all is well. However…
> Last time I looked at the store capturing code of C2 for optimizing initializing stores, it emits normal stores with barriers and then in InitializeNode::capture_store it finds stores that are initializing stores for the new object (C), finds and clones the plain store (excluding barriers), and cuts out the old initializing store with IGVN, which should make the IU barrier not ”useful” (only used by dead code), and hence be removed in the useless barrier elimination phase, having the implicit effect of eliding barriers on initializing stores. I think that was indeed the intention of cloning the plain store, and cutting out the old store + barriers. But that isn’t legal with IU. If this works, I don’t see how. Did I miss something?
Ok, this seems indeed problematic.
A way to get rid of it easily would be -XX:-ReduceFieldZeroing, right?
But I suspect that does more than is needed. I need to think about this
a little more.
Thanks for pointing this out!
/Roman
> /Erik
>
>> On 14 Jul 2022, at 22:11, Roman Kennke <rkennke at redhat.com> wrote:
>>
>> No wait. The barrier is elided when B is new, but when C is new. This works differently than SATB. Makes sense?
>>
>> Roman
>>
>>
>>> The problem is that when you store the edge from C to B in my example, at step 6, the newly allocated object C is implicitly live, but the new value being written to the field (B) is not implicitly live and needs to be explicitly kept alive in an IU model. But it isn’t if the barrier is elided. This is a difference to SATB where B is also implicitly live due to being part of the snapshot-at-the-beginning, which is why eliding the barrier has no effect with SATB.
>>> /Erik
>>>>> On 14 Jul 2022, at 21:49, Roman Kennke <rkennke at redhat.com> wrote:
>>>>
>>>> Hey Erik,
>>>>
>>>> I just had a closer look at the IU barriers again. First of all, we are *not* using the same barriers for IU and SATB. That's why disabling barrier elision does nothing. Instead, we emit a c2 node for the IU barrier, and optimize on that and expand it during barrier expansion pass (together with the LRBs). The barrier consumes and replaces values being stored into fields and array elements. If such a value is provably a newly created object, then we don't need the barrier there - the object is implicitely live already.
>>>>
>>>> Another difference between IU and SATB is that we don't need to keep-alive referents when somebody calls Reference.get() during concurrent marking. If such a referent goes out of scope again, then it's fine trivially. If the referent is stored anywhere, we will catch it with the barrier. If it only remains in a local variable by the end of concurrent marking, we will catch it in final remark. Please punch holes into this reasoning if you find anything. ;-)
>>>>
>>>> Thanks,
>>>> Roman
>>>>
>>>>
>>>>> Hi Roman,
>>>>> The problem isn’t the new object itself. It’s dangling pointers created from it. Sure the new object will be kept alive. The problem is when you store the last reference to *another* object, into a field in the new object, but without barriers that would normally keep that other object alive, as I described in the example. Then you are kind of toast. With SATB anything you stored into the new object is already guaranteed to be kept alive as it is either a new object (implicitly live), or something from the snapshot, which pre-write barriers ensure is fully remembered. The pre-write barrier would only find null which by definition has no business being marked.
>>>>> I will try to explain my example more clearly step by step.
>>>>> 1. There is an object A, reachable from a root of thread T1. A has a field pointing at another object B.
>>>>> 2. Marking starts. A is matked as a root, but before B is marked concurrently…
>>>>> 3. T1 loads B from the field of A.
>>>>> 4. T1 stores null to the field of A. B is now only reachable from a private root of T1, and is not visible to concurrent marking, and storing null did not keep B alive with IU barriers.
>>>>> 5. T1 allocates a new object C. It is implicitly alive and won’t be traced through.
>>>>> 6. T1 stores a reference from C to B. No barriers are used as C is a new object. (This is the illegal part causing a potential crash)
>>>>> 7. The root from T1 to B is discarded.
>>>>> 8. Concurrent marking traces from A but finds only null from step 4.
>>>>> 9. Marking terminates.
>>>>> You now have a dangling pointer from C to B that has not been marked through, and the marking has terminated. As I said, the elision in CMS was only valid because the entire young generation was traced again when old marking terminates, which would have found the reference from C to B, and kept it alive. But you don’t do that, so it seems like it will crash instead. That’s why store barrier elision on newly allocated objects in general is seemingly unsound with Shenandoah IU mode.
>>>>> Hope this helps.
>>>>> /Erik
>>>>>>> On 14 Jul 2022, at 20:21, Roman Kennke <rkennke at openjdk.org> wrote:
>>>>>>
>>>>>> On Thu, 14 Jul 2022 17:40:27 GMT, Erik Österlund <eosterlund at openjdk.org> wrote:
>>>>>>
>>>>>>>>> Speaking of IU mode, how does Shenandoah IU mode deal with barrier elision on newly allocated objects? It was only valid in CMS because the entire young generation was traversed when terminating the concurrent marking, so that unvisited pointers could be found.
>>>>>>>>
>>>>>>>>
>>>>>>>> Shenandoah SATB/IU both elide barrier on newly allocated objects. SATB barrier should guarantee they can only refer marked objects. Do I miss anything?
>>>>>>>
>>>>>>> Yes, I believe so. For a SATB collector, it is okay to elide the barrier on newly allocated objects, because they were not part of the snapshot-of-the-beginning, and all new objects are implicitly alive and don't need to be visited.
>>>>>>>
>>>>>>> However, with an IU scheme, that property does not translate.
>>>>>>>
>>>>>>> Consider that you have a particular object graph when marking starts, then one mutator loads an object from the graph, and clears the field such that it is only reachable from the roots of said murator thread. Then the mutator writes the object to a newly allocated object without barriers and discards the root. Now, the reference is only reachable from the newly allocated object.
>>>>>>>
>>>>>>> So for this elision to be valid with IU, the GC has to visit newly allocated objects again before terminating, which e.g. CMS indeed did, by doing a young collection in the safepoint that terminated old marking, but I don't know that Shenandoah does anything like that.
>>>>>>
>>>>>> IIRC, in Shenandoah's IU mode we treat new objects as implicitely alive, much like we do with SATB mode. Therefore it should be good to elide the barriers there. I know that this is not the classical I-U scheme that I believe CMS followed. There has been a paper (I believe it is that paper [1]) that showed that it's orthogonal issues whether we follow new or old references and whether or not we treat new objects alive or not. Treating new objects as live has the advantage that it solves the termination problem.
>>>>>>
>>>>>> [1] https://urldefense.com/v3/__https://www.cs.technion.ac.il/*yahave/papers/pldi06-cgc.pdf__;fg!!ACWV5N9M2RV99hQ!LsTZM-MExdpbuTsR32dxyaa5i0r_hGg6RUCZm_YoqDYChH9oqK-dpzVCb7nD-ZtT0hlRzSgDnXxLrSmkx7Q$
>>>>>> -------------
>>>>>>
>>>>>> PR: https://git.openjdk.org/jdk19/pull/140
>>>>
>>
More information about the hotspot-gc-dev
mailing list