8216541: CompiledICHolders of VM locked unloaded nmethods are released too late

Erik Österlund erik.osterlund at oracle.com
Mon Jan 28 13:56:28 UTC 2019


Hi,

There is a bit of an anomaly how we deal with freeing up resources used 
by inline caches when an nmethod dies.

An inline cache might have an associated ICStub if it is in a 
transitional state. But it might also have a CompiledICHolder if it is 
referring to a c2i adapter or an itable stub. These resources need to be 
freed up when an nmethod dies. But they get cleared at completely 
different times.

As for IC stubs, they get "cleared" when is_alive() nmethods get 
converted to zombie. As for nmethods being made unloaded, they will 
(presumably) not have IC caches in transitional states.

As for CompiledICHolders, rather than clearing them out when the nmethod 
dies (transitions to zombie or unloaded), as we did with IC stubs, we 
defer clearing of CompiledICHolders until the nmethod gets deleted, or 
"flushed". Because reasons.

However, unless precaution is taken, it is not safe to find and clear 
out CompiledICHolders when the nmethod gets deleted (as opposed to when 
the nmethod died), due to how we detect there is a CompiledICHolder 
associated with the CompiledIC. We look at the destination of the call 
instruction, and ask said code blob if it is an adapter blob. If it is 
an adapter blob, it is assumed that the CompiledIC must have a 
CompiledICHolder associated with it. What can happen then is that 
between the nmethod dying (in particular due to becoming unloaded), and 
the nmethod being freed, the slot in the CodeCache that the stale 
destination pointed at, has had a c2i adapter allocated over it. So 
asking the question whether a CompiledIC has a CompiledICHolder is not 
safe in general, if the destination is stale, and points at dead 
nmethods. It will then have false positives.

In order to deal with that awkwardness, rather than clearing 
CompiledICHolders when the nmethod dies and it is perfectly safe to 
query whether it has a CompiledICHolder or not, we insist on deferring 
clearing of CompiledICHolders to when the nmethod dies. To make that 
safe and avoid the false positives, the sweeper cleans CompiledICs of 
unloaded nmethods... most of the time...
...except when they are locked in VM. Then we don't do that.

So when the sweeper finds unloaded nmethods that are locked in VM, then 
it skips cleaning of CompiledICs one cycle, its (stale) destination 
possibly gets reclaimed and possibly has a c2i adapter allocated in the 
same memory location as that stale destination points at. When we 
subsequently "flush" the nmethod, we incorrectly find that it has a 
CompiledICHolder (due to its destination pointing at a c2i adapter 
blob), even though it never had a CompiledICHolder when it died. Ouch. 
It goes downhill from there.

It happens to be that when using code loading/unloading JVMTI events, 
this bug is more easily provoked. The reason for this is that the events 
lock the nmethods with the nmethodLocker until some event is being 
processed in the service thread, making the sweeper skip over cleaning 
the ICs of the unloaded nmethods in this corner case of the nmethod life 
cycle.

My proposed solution to this bug is to simply nuke all CompiledIC 
metadata, whether that is ICStubs or CompiledICHolders, the instant the 
nmethod dies (transitions to zombie or unloaded), as well as setting 
state of such CompiledICs to clean. It seems much more fragile to have 
to maintain dead CompiledICs only for the sake of releasing 
CompiledICHolders when freeing the nmethod rather than when it dies. It 
also seems unnecessarily complicated to free CompiledICHolders and 
ICStubs of CompiledICs to get cleared at different times. By just 
clearing all of this when the nmethods die, we remove this complexity.

Bug:
https://bugs.openjdk.java.net/browse/JDK-8216541

Webrev:
http://cr.openjdk.java.net/~eosterlund/8216541/webrev.00/

Going forward, I intend to perform a bunch of cleanups in this area, but 
I wanted to keep the change small ish for the bug fix, to make it easier 
to backport. It will probably need to get backported pretty far back. I 
don't think this has ever worked I'm afraid.

The proposed change has survived 200 rounds of kitchensink, hs-tier1-3 
and hs-precheckin-comp.

Thanks,
/Erik


More information about the hotspot-dev mailing list