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

Peter Levart peter.levart at gmail.com
Sat May 9 09:33:43 UTC 2015

Hi Vladimir,

On 05/08/2015 07:16 PM, Vladimir Ivanov wrote:
> 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.

> 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 

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?

> 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?

> 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