On 19.05.2016 21:32, Peter Levart wrote:
[...]
a ClassValue instance can be thought of as a component of a compound
key. Together with a Class, they form a tuple (aClass, aClassValue) that
can be associated with an "associated value", AV. And yes, the AVs
associated with tuple containing a particular Class are strongly
reachable from that Class.

I see, I was mixing ClassValue and AV, I was more talking about AV, than ClassValue itself, though ClassValue surely plays an important role in here. Anyway, I am going for that AV is the value computed by aClassValue.

You said that the AV is strongly reachable from aClass. Can I further assume, that aClassValue is not strongly reachable from aClass? And that aClassValue can be collected independent of aClass? Can I further assume, that aClassValue can be collected even if AVs for it continue to exist?

[...]
An AV associated with a tuple (Integer.TYPE, aClassValue) -> AV can be
garbage collected. But only if aClassValue can be garbage collected 1st.

hmm... so in (aClass, aClassValue)->AV if aClassValue can be collected, AV can, but not the other way around... what about aClass? if nothing but AV is referencing aClass, can AV be garbage collected, even if aClassValue cannot? Can I extend your statement to AV can be collected only if either aClass or aClassValue can be garbage collected first?

Let us assume this is the case for now.

This is the most tricky part to get right in order to prevent leaks. If
in above example, aClassValue is reachable from the AV, then we have a
leak. The reachability of a ClassValue instance from the associated
value AV is not always obvious. One has to take into account the
following non-obvious references:

1 - each object instance has an implicit reference to its implementing class
2 - each class has a reference to its defining ClassLoader
3 - each ClassLoader has a reference to all classes defined by it
(except VM annonymous classes)
4 - each ClassLoader has a reference to all its predecessors (that it
delegates to)

Since a ClassValue instance is typically assigned to a static final
field, such instance is reachable from the class that declares the field.

I think you can get the picture from that.

yeah... that is actually problematic. Because if I keep no hard reference the ClassValue can be collected, even if the AVs still exist... meaning they would become unreachable. And if I keep one I have a memory leak... well more about in the program you have shown me later on.

[...]
Ok, let's set up the stage. If I understand you correctly, then:

Groovy runtime is loaded by whatever class loader is loading the
application (see the comment in MetaClass constructor if this is not
true).  This is either the ClassLoader.getSystemClassLoader() (the APP
class loader) if started from command line or for example Web App class
loader in a Web container.

Well, actually... if you start a script on the command line, the loader is a child to the app class loader, when used as library it could be the app loader (for example if the groovy program is precompiled) and in a tomcat like scenario it could be either the class loader for the web app, or the loader for all web apps. But let's go with the cases you mentioned first ;)

MetaClass(es) are objects implemented by Groovy runtime class(es). Let's
call them simply MetaClass.

good

Here's how I would do that:


public class MetaClass {

     // this list keeps MetaClass instances strongly reachable from the 
MetaClass
     // class(loader) since they are only weakly reachable from their associated
     // Class(es)
     private static final ArrayList<MetaClass> META_CLASS_LIST = new 
ArrayList<>();

     // this WeakReference is constructed so that it keeps a strong reference
     // to a referent until releaseStrong() is called
     private static final class WeakEntry extends WeakReference<MetaClass> {
         private final AtomicReference<MetaClass> strong;

         WeakEntry(MetaClass mc) {
             super(mc);
             strong = new AtomicReference<>(mc);
         }

         boolean releaseStrong() {
             MetaClass mc = strong.get();
             return mc != null && strong.compareAndSet(mc, null);
         }
     }

     private static final ClassValue<WeakEntry> WEAK_ENTRY_CV =
         new ClassValue<WeakEntry>() {
             @Override
             protected WeakEntry computeValue(Class<?> type) {
                 return new WeakEntry(new MetaClass(type));
             }
         };

     // the public API
     public MetaClass getInstanceFor(Class<?> type) {
         WeakEntry entry = WEAK_ENTRY_CV.get(type);
         MetaClass mc = entry.get();
         if (entry.releaseStrong()) {
             synchronized (META_CLASS_LIST) {
                 META_CLASS_LIST.add(mc);
             }
         }
         return mc;
     }

     MetaClass(Class<?> type) {
         // derive it from 'type', but don't reference it
         // strongly if Groovy runtime is loaded by a parent
         // class loader of the application class loader
         // that loads application classes  you want
         // MetaClass(es) to be derived from.
     }
}


This will keep MetaClass instances isolated from the associated classes
but still keep them reachable as long as the
MetaClass.class.getClassLoader() is alive.

Is this going to help?

partially... MetaClass will realistically have a strong reference to the class the meta class is for and a lot of other classes. I will need to reference methods and fields by reflection and at some point keep them referenced. Through the declaring class I should then again have a strong reference to the class.

1_000_000.times { Eval.me("42") }

this will create 1 million classes, in 1 million classloaders, which are a child to the class loader for the Groovy runtime. Each of them will get a meta class... ignoring the list, if the strong reference to the script class in MetaClass is here enough to prevent collection, then this is a problem. The list of course is also a problem, since keeping them alive as long as MetaClass.class.getClassLoader is alive, is actually not the semantics I need.

The semantics I need is something like lifetime(AV) is roughly min(lifetime(aClass),lifetime(aClassValue)), with AV referencing aClass not influencing it being collectable, as long as this association is the only reference to AV. If it is the max of them I am in trouble.

And seeing how aClass kind of has a strong reference to AV and that if aClassValue and AV are from the same loader and if AV has a strong reference to aClass as well, it will cause aClass not being collectable for as long as the loader for aClassValue and AV exists... because aClassValue is a static value in one of the classes, and AV is not collected before aClassValue is collected and aClass is not not collected because of the reference in AV.

The "old" solution in Groovy is to keep a "global" map with weak class keys and soft references to MetaClass. This allows in theory the class being collectable, but only if the MetaClass has been collected before.. and some VMs really do not like soft referenced classes much (forced us to use the mark and sweep gc for example). Also soft references used not always to be clean up when permgen space is low. But if they are not cleaned up, then MetaClass prevents the garbage collection of the class, causing permgen problems. Not sure how that is with meta space, but from what I have seen so far, this can still happen. And of course using SoftReference means we garbage collect relatively late, and cause a big garbage collection. I cannot use weak references, because tend to be collected to fast and creating the meta class is not exactly a cheap operation.

bye Jochen

_______________________________________________
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

Reply via email to