[9] RFR (M): 8079205: CallSite dependency tracking is broken after sun.misc.Cleaner became automatically cleared

Vladimir Ivanov vladimir.x.ivanov at oracle.com
Tue May 12 10:56:04 UTC 2015


>> http://cr.openjdk.java.net/~vlivanov/8079205/webrev.01
>> https://bugs.openjdk.java.net/browse/JDK-8079205
> Your Finalizator touches are good. Supplier interface is not needed as
> there is a public Reference superclass that can be used for return type
> of JavaLangRefAccess.createFinalizator(). You can remove the import for
> Supplier in Reference now.
Thanks for spotting that. Will do.

>> Recent change in sun.misc.Cleaner behavior broke CallSite context
>> cleanup.
>> CallSite references context class through a Cleaner to avoid its
>> unnecessary retention.
>> The problem is the following: to do a cleanup (invalidate all affected
>> nmethods) VM needs a pointer to a context class. Until Cleaner is
>> cleared (and it was a manual action, since Cleaner extends
>> PhantomReference isn't automatically cleared according to the docs),
>> VM can extract it from CallSite.context.referent field.
> If PhantomReference.referent wasn't cleared by VM when PhantomReference
> was equeued, could it happen that the referent pointer was still != null
> and the referent object's heap memory was already reclaimed by GC? Is
> that what JDK-8071931 is about? (I cant't see the bug - it's internal).
> In that respect the Cleaner based solution was broken from the start, as
> you did dereference the referent after Cleaner was enqueued. You could
> get a != null pointer which was pointing to reclaimed heap. In Java this
> dereference is prevented by PhantomReference.get() always returning null
> (well, nothing prevents one to read the referent field with reflection
> though).
No, the object isn't reclaimed until corresponding reference is != null.
When Cleaner wasn't automatically cleared, it was guaranteed that the 
referent is alive when cleanup action happens. That is the assumption 
CallSite dependency tracking is based on.

It's not the case anymore when Cleaner is automatically cleared. Cleanup 
action can happen when context Class is already GCed. So, I can access 
neither the context mirror class nor VM-internal InstanceKlass.

> FinalReference(s) are different in that their referent is not reclaimed
> while it is still reachable through FinalReference, which means that
> finalizable objects must undergo at least two GC cycles, with processing
> in the reference handler and finalizer threads inbetween the cycles, to
> be reclaimed. PhantomReference(s), on the other hand, can be enqueued
> and their referents reclaimed in the same GC cycle, can't they?
Since PhantomReference isn't automatically cleared, GC can't reclaim its 
referent until the reference is manually cleared or PhantomReference 
becomes unreachable as a result of cleanup action.
So, it's impossible to reclaim it in the same GC cycle (unless it is a 
concurrent GC cycle?).

>> I experimented with moving cleanup logic into VM [1],
> What's the downside of that approach? I mean, why is GC-assisted
> approach better? Simpler?
IMO the more code on JDK side the better. More safety guarantees are 
provided in Java code comparing to native/VM code.

Also, JVM-based approach suffers from the fact it doesn't get prompt 
notification when context Class can be GCed. Since it should work with 
autocleared references, there's no need in a cleanup action anymore.
By the time cleanup happens, referent can be already GCed.

That's why I switched from Cleaner to WeakReference. When 
CallSite.context is cleared ("stale" context), VM has to scan all 
nmethods in the code cache to find all affected nmethods.

BTW I had a private discussion with Kim Barrett who suggested an 
alternative approach which doesn't require full code cache scan. I plan 
to do some prototyping to understand its feasibility, since it requires 
non-InstanceKlass-based nmethod dependency tracking machinery.

Best regards,
Vladimir Ivanov

>> but Peter Levart came up with a clever idea and implemented
>> FinalReference-based cleaner-like Finalizator. Classes don't have
>> finalizers, but Finalizator allows to attach a finalization action to
>> them. And it is guaranteed that the referent is alive when
>> finalization happens.
>> Also, Peter spotted another problem with Cleaner-based implementation.
>> Cleaner cleanup action is strongly referenced, since it is registered
>> in Cleaner class. CallSite context cleanup action keeps a reference to
>> CallSite class (it is passed to MHN.invalidateDependentNMethods).
>> Users are free to extend CallSite and many do so. If a context class
>> and a call site class are loaded by a custom class loader, such loader
>> will never be unloaded, causing a memory leak.
>> Finalizator doesn't suffer from that, since the action is referenced
>> only from Finalizator instance. The downside is that cleanup action
>> can be missed if Finalizator becomes unreachable. It's not a problem
>> for CallSite context, because a context is always referenced from some
>> CallSite and if a CallSite becomes unreachable, there's no need to
>> perform a cleanup.
>> Testing: jdk/test/java/lang/invoke, hotspot/test/compiler/jsr292
>> Contributed-by: plevart, vlivanov
>> Best regards,
>> Vladimir Ivanov
>> PS: frankly speaking, I would move Finalizator from java.lang.ref to
>> java.lang.invoke and call it Context, if there were a way to extend
>> package-private FinalReference from another package :-)
> Modules may have an answer for that. FinalReference could be moved to a
> package (java.lang.ref.internal or jdk.lang.ref) that is not exported to
> the world and made public.
> On the other hand, I don't see a reason why FinalReference couldn't be
> part of JDK public API. I know it's currently just a private mechanism
> to implement finalization which has issues and many would like to see it
> gone, but Java has learned to live with it, and, as you see, it can be a
> solution to some problems too ;-)
> Regards, Peter
>> [1] http://cr.openjdk.java.net/~vlivanov/8079205/webrev.00

More information about the core-libs-dev mailing list