RFR: 8210498: nmethod entry barriers

Per Liden per.liden at oracle.com
Mon Oct 8 06:13:06 UTC 2018


On 2018-10-05 17:07, Erik Österlund wrote:
> Hi,
> 
> In order to implement concurrent class unloading for ZGC, a mechanism 
> will be required to arm nmethods in a safepoint such that the first 
> entry into an nmethod after the pause triggers a barrier before continuing.
> 
> The barrier will be used for:
>    * Patching immediate oops, and keeping phantomly reachable oops alive 
> through the nmethod entry
>    * Catching calls from stale IC caches (due to class unloading) into 
> nmethods that are being unloaded (containing broken oops and metadata), 
> and lazily clean the IC cache and re-resolve the call.
> 
> This implementation is for x86_64 and is based on the prototyping work 
> of Rickard Bäckman. So big thanks to Rickard, who will co-author of this 
> patch.
> The way it works is that there is a cmp #offset(r15_thread), 
> simm32_epoch); je #continuation; inserted into the verified entry of 
> nmethods that conditionally calls a stub trampolining into the VM.
> When the simm32_epoch is not equal to some TLS-local field, then a slow 
> path is taken to run some barrier inside of the VM.
> By changing this TLS-local field in the safepoint, all verified entries 
> will take the slow path into the VM when run, where the barrier is 
> eventually disarmed by patching the simm32_epoch value of the 
> instruction stream to the current globally correct value.
> 
> In terms of abstractions, to utilize this mechanism, you need to create 
> your BarierSet with an BarrierSetNMethod helper object, just like the 
> BarrierSetAssembler, BarrierSetC1 and BarrierSetC2. The 
> BarrierSetNMethod base class handles the barrier itself. It has a 
> virtual function, nmethod_entry_barrier, which gets called by the slow 
> paths. So each user of this mechanism inherits from BarrierSetNMethod 
> and puts its barrier in there.
> 
> The OSR nmethods do not have a verified entry. Therefore, the barrier is 
> run explicitly in the runtime before calling into OSR nmethods to 
> achieve the same effect of trapping new calls into nmethods.
> 
> The nmethod entry barrier returns true/false depending on whether the 
> called nmethod may be entered or not. This allows lazily cleaning 
> nmethod IC caches during unloading, which invokes the same helper method 
> that is patched into the verified entry when made not entrant.
> 
> Eventually, I would like to also use this mechanism to remove the 
> nmethod hotness counter maintenance we today perform in safepoint 
> cleanup instead. These nmethod entry barriers can provide better 
> heuristics for whether nmethods are used or not. But that would require 
> buy-in from maintainers of other platforms. So let's take one step at a 
> time.
> 
> For the curious reader thinking that this might be an expensive 
> operation, it is not. This check in the nmethod verified entry is very 
> predictable and cheap. And because of use of inlining, the checks become 
> more infrequent. In my evaluation, it has gone completely under the 
> radar in benchmarking (running this with my current version of 
> concurrent class unloading in ZGC).
> 
> Webrev:
> http://cr.openjdk.java.net/~eosterlund/8210498/webrev.00/

I reviewed this before Erik sent it out, so my comments have already 
been addressed. Just found one additional thing, the BarrierSet 
constructor now looks like this:

96   BarrierSet(BarrierSetAssembler* barrier_set_assembler,
97              BarrierSetC1* barrier_set_c1,
98              BarrierSetC2* barrier_set_c2,
99              const FakeRtti& fake_rtti,
100             BarrierSetNMethod* barrier_set_nmethod = NULL) :
101     _fake_rtti(fake_rtti),
102     _barrier_set_assembler(barrier_set_assembler),
103     _barrier_set_c1(barrier_set_c1),
104     _barrier_set_c2(barrier_set_c2),
105     _barrier_set_nmethod(barrier_set_nmethod) {}

I find it odd that the fake_rtti argument comes in the middle of the 
barrier_set_* arguments. I can see that you don't want GC barrier sets 
to have to supply NULL if they don't have a nmethod barrier set, but 
maybe we can have two constructors instead, like:

BarrierSet(BarrierSetAssembler* barrier_set_assembler,
            BarrierSetC1* barrier_set_c1,
            BarrierSetC2* barrier_set_c2,
            const FakeRtti& fake_rtti) :
     _fake_rtti(fake_rtti),
     _barrier_set_assembler(barrier_set_assembler),
     _barrier_set_c1(barrier_set_c1),
     _barrier_set_c2(barrier_set_c2),
     _barrier_set_nmethod(NULL) {}

BarrierSet(BarrierSetAssembler* barrier_set_assembler,
            BarrierSetC1* barrier_set_c1,
            BarrierSetC2* barrier_set_c2,
            BarrierSetNMethod* barrier_set_nmethod,
            const FakeRtti& fake_rtti,) :
     _fake_rtti(fake_rtti),
     _barrier_set_assembler(barrier_set_assembler),
     _barrier_set_c1(barrier_set_c1),
     _barrier_set_c2(barrier_set_c2),
     _barrier_set_nmethod(barrier_set_nmethod) {}

What do you think?

Other than that, this looks good to me.

cheers,
Per

> 
> Bug ID:
> https://bugs.openjdk.java.net/browse/JDK-8210498
> 
> Thanks,
> /Erik


More information about the hotspot-dev mailing list