Hi Vladimir, I am not an export in the HS area but the code mostly made sense to me. I also like Peter's suggestion of Context implementing Runnable.
Some minor comments. CallSite.java: 145 private final long dependencies = 0; // Used by JVM to store JVM_nmethodBucket* It's a little odd to see this field be final, but i think i understand the motivation as it's essentially acting as a memory address pointing to the head of the nbucket list, so in Java code you don't want to assign it to anything other than 0. Is VM access, via java_lang_invoke_CallSite_Context, affected by treating final fields as really final in the j.l.i package? javaClasses.hpp: 1182 static oop context(oop site); 1183 static void set_context(oop site, oop context); Is set_context required? instanceKlass.cpp: 1876 // recording of dependecies. Returns true if the bucket is ready for reclamation. typo "dependecies" Paul. On May 13, 2015, at 11:27 AM, Vladimir Ivanov <vladimir.x.iva...@oracle.com> wrote: > I finished experimenting with the idea inspired by private discussion with > Kim Barrett: > > http://cr.openjdk.java.net/~vlivanov/8079205/webrev.02/ > > The idea is to use CallSite instance as a context for dependency tracking, > instead of the Class CallSite is bound to. It requires extension of nmethod > dependencies to be used separately from InstanceKlass. Though it slightly > complicates nmethod dependency management (special cases for > call_site_target_value are added), it completely eliminates the need for > context state transitions. > > Every CallSite instance has its dedicated context, represented as > CallSite.Context which stores an address of the dependency list head > (nmethodBucket*). Cleanup action (Cleaner) is attached to CallSite instance > and frees all allocated nmethodBucket entries when CallSite becomes > unreachable. Though CallSite instance can freely go away, the Cleaner keeps > corresponding CallSite.Context from reclamation (all Cleaners are registered > in static list in Cleaner class). > > I like how it finally shaped out. It became much easier to reason about > CallSite context. Dependency tracking extension to non-InstanceKlass > contextes looks quite natural and can be reused. > > Testing: extended regression test, jprt, nashorn > > Thanks! > > Best regards, > Vladimir Ivanov > > On 5/12/15 1:56 PM, Vladimir Ivanov wrote: >> Peter, >> >>>> 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 >>>