RFR: 8209189: Make CompiledMethod::do_unloading more concurrent

Erik Österlund erik.osterlund at oracle.com
Fri Oct 26 18:51:20 UTC 2018


Hi Coleen,

On 2018-10-26 19:31, coleen.phillimore at oracle.com wrote:
> 
> 
> On 10/26/18 12:25 PM, Erik Österlund wrote:
>> Hi Coleen,
>>
>> Thank you for reviewing this.
>>
>> On 2018-10-26 17:58, coleen.phillimore at oracle.com wrote:
>>>
>>> I think this looks really good. I appreciate the cleanup in this 
>>> complexity.  It's nice that nmethod/CompiledMethod as non-gc 
>>> structure with oops, there's simply an oops_do() vs. all these 
>>> do_unloading_scopes() functions.   It reduces the special handling. I 
>>> spent a lot of time trying to figure out how this "postponed" 
>>> cleaning works.  This make a lot of sense.
>>
>> I'm glad you like it! I also think the new model is more intuitive.
>>
>>> http://cr.openjdk.java.net/~eosterlund/8209189/webrev.00/src/hotspot/share/code/compiledMethod.cpp.udiff.html 
>>>
>>>
>>> 508 bool result = IsUnloadingBehaviour::current()->is_unloading(this);
>>>
>>>
>>> This line is essentially the special sauce to this change.  What is 
>>> hard to see immediately is that this doesn't just test the flag but 
>>> will do an oops_do in order to set the flag.  Can you add a comment 
>>> to this effect here?
>>
>> Done.
>>
>> New incremental (including Vladimir's suggestion to remove now dead 
>> code):
>> http://cr.openjdk.java.net/~eosterlund/8209189/webrev.00_01/
>>
>> Full:
>> http://cr.openjdk.java.net/~eosterlund/8209189/webrev.01/
>>
>>
>>> Also, two GC threads could be visiting the same nmethods at the same 
>>> time with this cleaning.  This is ok though as long as they set the 
>>> is_unloading state correctly.
>>
>> Exactly. The value will monotonically change, possibly by multiple 
>> threads (but in practice mostly by one thread) to the same value. If 
>> two threads race, they will race for writing the same value, which is 
>> harmless.
>>
>>> 500 state._value = RawAccess<MO_RELAXED>::load(&_is_unloading_state);
>>>
>>>
>>> Are these required or are you being pedantic?  Shouldn't it be 
>>> ordered for multiple GC threads?
>>
>> No, ordering is irrelevant here. What is relevant is that the access 
>> is atomic; that's why I use MO_RELAXED, which gives me those guarantees.
>>
>> As mentioned previously, two threads may race, and that is harmless; 
>> they will race for writing the same cached value. There are no 
>> ordering constraints.
> 
> So is RawAccess<MO_RELAXED> the same as:
> 
> state._value = _is_unloading_state; ?

Yes basically. It's equivalent to Atomic::store, which is (currently) 
equivalent to a normal volatile store as you say. Except I managed to 
use way more templates to express the same thing in a semantically more 
satisfying way. And that makes me feel a lot better. I'm not biased...

After Kim's C++14 upgrades, it will map to memory_order_relaxed accesses 
(hence the name MO_RELAXED), as volatile accesses don't really guarantee 
atomicity according to the C++14 standard. In fact, it will be 
explicitly undefined behaviour then.

>>
>>> http://cr.openjdk.java.net/~eosterlund/8209189/webrev.00/src/hotspot/share/gc/shared/gcBehaviours.new.hpp.html 
>>>
>>>
>>> Despite the fact that you spelled "behavoirs" wrong, I think I'm 
>>> warming up to this use of the concept here.  I'm having trouble 
>>> thinking of a more specific word for it.
>>
>> Sorry Coleen, but "behaviours" is the correct English spelling. 
>> American English is not English. Shots fired...
> 
> :)
>>
>>> I have to confess to not reviewing the epoch handling very well.
>>>
>>> Since there's a big alignment gap in CompiledMethod, why not just 
>>> have two fields and skip this odd union?  One bool for is_unloading 
>>> and one short for unloading_cycle?  Reading the word _inflated is 
>>> strange here.
>>
>> I use the union to make sure that both values are accessed as a pair 
>> atomically. The union allows me to represent the same data either as a 
>> single value that can be accessed atomically, and then interpret it as 
>> an "inflated" view where it comprises a tuple representing both an 
>> epoch and result value. The single value is used to ensure atomicity, 
>> and the inflated view is used for interpreting what the state represents.
> 
> Can you put some sort of comment above the IsUnloading{Union,Struct} to 
> that effect?  I don't need to see it.  I'm sure your English will be 
> good :)

Will do!

Thanks for the review.

/Erik

> Coleen
> 
>>
>> Thanks,
>> /Erik
>>
>>> Thanks,
>>> Coleen
>>>
>>> On 10/19/18 10:22 AM, Erik Österlund wrote:
>>>> Hi,
>>>>
>>>> Today the nmethods are unloaded either serially or in parallel due 
>>>> to GC (and e.g. class unloading). With ZGC concurrent class 
>>>> unloading, a concurrent fashion is required. Rather than inventing 
>>>> yet a third way of unloading nmethods due to class unloading, it 
>>>> would be ideal if there could be one unified way of doing it that 
>>>> makes everyone happy.
>>>>
>>>> To solve this problem in a more general way, a new member function 
>>>> on CompiledMethod is added: is_unloading(). It returns whether a 
>>>> CompiledMethod has broken oops. In order to not have to iterate over 
>>>> all the oops every time this question is asked, it caches the 
>>>> result, so it needs to be computed only once per "epoch". Every time 
>>>> a CodeCache unloading is triggered by the GC, a new epoch is 
>>>> started, which forces re-computation of whether the CompiledMethod 
>>>> is_unloading() the first time it is called.
>>>>
>>>> So by having is_unloading() be lazily evaluated, it is now possible 
>>>> to build a do_unloading() method on nmethod that simply makes the 
>>>> nmethod unloaded if it is_unloading(), and otherwise cleans the 
>>>> various caches. Cleaning e.g. the inline caches of said nmethod, 
>>>> uses the same is_unloading() method on its targets to figure out if 
>>>> the IC cache should be cleaned or not. Again, the epoched caching 
>>>> mechanism makes sure we don't recompute this value.
>>>>
>>>> The new do_unloading() method may be called in both serial, parallel 
>>>> and concurrent contexts. So I removed the parallel variation of this 
>>>> that we had before, that unnecessarily postponed the unloading due 
>>>> to not having computed whether the nmethod should be unloaded or 
>>>> not. Since that is now computed on-demand lazily, there is no longer 
>>>> a need for postponing this, nor to have two phases for parallel 
>>>> nmethod unloading.
>>>>
>>>> While there is more work involved in making concurrent nmethod 
>>>> unloading work, this is a good cleanup towards that goal.
>>>>
>>>> Bug ID:
>>>> https://bugs.openjdk.java.net/browse/JDK-8209189
>>>>
>>>> Webrev:
>>>> cr.openjdk.java.net/~eosterlund/8209189/webrev.00/
>>>>
>>>> Thanks,
>>>> /Erik
>>>
> 


More information about the hotspot-dev mailing list