8079205: CallSite dependency tracking is broken after sun.misc.Cleaner became automatically cleared
Reviewed-by: roland, psandoz, plevart, kbarrett, jrose
This commit is contained in:
parent
937fb712ee
commit
6652d6ac33
@ -27,8 +27,6 @@ package java.lang.invoke;
|
|||||||
|
|
||||||
import static java.lang.invoke.MethodHandleStatics.*;
|
import static java.lang.invoke.MethodHandleStatics.*;
|
||||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import sun.misc.Cleaner;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code CallSite} is a holder for a variable {@link MethodHandle},
|
* A {@code CallSite} is a holder for a variable {@link MethodHandle},
|
||||||
@ -138,47 +136,9 @@ public class CallSite {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code CallSite} dependency context.
|
* {@code CallSite} dependency context.
|
||||||
* VM uses context class to store nmethod dependencies on the call site target.
|
* JVM uses CallSite.context to store nmethod dependencies on the call site target.
|
||||||
* Can be in 2 states: (a) null; or (b) {@code Cleaner} instance pointing to some Class instance.
|
|
||||||
* Lazily initialized when CallSite instance is linked to some indy call site or VM needs
|
|
||||||
* it to store dependencies. As a corollary, "null" context means there are no dependencies
|
|
||||||
* registered yet. {@code Cleaner} is used in 2 roles:
|
|
||||||
* (a) context class access for VM;
|
|
||||||
* (b) stale context class cleanup.
|
|
||||||
* {@code Cleaner} holds the context class until cleanup action is finished (see {@code PhantomReference}).
|
|
||||||
* Though it's impossible to get the context class using {@code Reference.get()}, VM extracts it directly
|
|
||||||
* from {@code Reference.referent} field.
|
|
||||||
*/
|
*/
|
||||||
private volatile Cleaner context = null;
|
private final MethodHandleNatives.CallSiteContext context = MethodHandleNatives.CallSiteContext.make(this);
|
||||||
|
|
||||||
/**
|
|
||||||
* Default context.
|
|
||||||
* VM uses it to initialize non-linked CallSite context.
|
|
||||||
*/
|
|
||||||
private static class DefaultContext {}
|
|
||||||
private static final Cleaner DEFAULT_CONTEXT = makeContext(DefaultContext.class, null);
|
|
||||||
|
|
||||||
private static Cleaner makeContext(Class<?> referent, final CallSite holder) {
|
|
||||||
return Cleaner.create(referent,
|
|
||||||
new Runnable() {
|
|
||||||
@Override public void run() {
|
|
||||||
MethodHandleNatives.invalidateDependentNMethods(holder);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Initialize context class used for nmethod dependency tracking */
|
|
||||||
/*package-private*/
|
|
||||||
void initContext(Class<?> newContext) {
|
|
||||||
// If there are concurrent actions, exactly one succeeds.
|
|
||||||
if (context == null) {
|
|
||||||
UNSAFE.compareAndSwapObject(this, CONTEXT_OFFSET, /*expected=*/null, makeContext(newContext, this));
|
|
||||||
// No need to care about failed CAS attempt.
|
|
||||||
// Since initContext is called from indy call site linkage in newContext class, there's no risk
|
|
||||||
// that the context class becomes dead while corresponding context cleaner is alive (causing cleanup
|
|
||||||
// action in the wrong context).
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the type of this call site's target.
|
* Returns the type of this call site's target.
|
||||||
|
@ -30,6 +30,7 @@ import java.lang.reflect.Field;
|
|||||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||||
import static java.lang.invoke.MethodHandleStatics.*;
|
import static java.lang.invoke.MethodHandleStatics.*;
|
||||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||||
|
import sun.misc.Cleaner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The JVM interface for the method handles package is all here.
|
* The JVM interface for the method handles package is all here.
|
||||||
@ -61,8 +62,27 @@ class MethodHandleNatives {
|
|||||||
static native void setCallSiteTargetNormal(CallSite site, MethodHandle target);
|
static native void setCallSiteTargetNormal(CallSite site, MethodHandle target);
|
||||||
static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target);
|
static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target);
|
||||||
|
|
||||||
/** Invalidate CallSite context: clean up dependent nmethods and reset call site context to initial state (null). */
|
/** Represents a context to track nmethod dependencies on CallSite instance target. */
|
||||||
static native void invalidateDependentNMethods(CallSite site);
|
static class CallSiteContext implements Runnable {
|
||||||
|
//@Injected JVM_nmethodBucket* vmdependencies;
|
||||||
|
|
||||||
|
static CallSiteContext make(CallSite cs) {
|
||||||
|
final CallSiteContext newContext = new CallSiteContext();
|
||||||
|
// Cleaner is attached to CallSite instance and it clears native structures allocated for CallSite context.
|
||||||
|
// Though the CallSite can become unreachable, its Context is retained by the Cleaner instance (which is
|
||||||
|
// referenced from Cleaner class) until cleanup is performed.
|
||||||
|
Cleaner.create(cs, newContext);
|
||||||
|
return newContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
MethodHandleNatives.clearCallSiteContext(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Invalidate all recorded nmethods. */
|
||||||
|
private static native void clearCallSiteContext(CallSiteContext context);
|
||||||
|
|
||||||
private static native void registerNatives();
|
private static native void registerNatives();
|
||||||
static {
|
static {
|
||||||
@ -235,7 +255,6 @@ class MethodHandleNatives {
|
|||||||
return Invokers.linkToTargetMethod(type);
|
return Invokers.linkToTargetMethod(type);
|
||||||
} else {
|
} else {
|
||||||
appendixResult[0] = callSite;
|
appendixResult[0] = callSite;
|
||||||
callSite.initContext(caller);
|
|
||||||
return Invokers.linkToCallSiteMethod(type);
|
return Invokers.linkToCallSiteMethod(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user