Version 20

    Writing efficient Class Cache

     

    It is a tricky thing to keep classes on cache. If you keep any static reference to a ClassLoader that will prevent the JVM to unload unused classes. This is usually knowed as redeployment leakages, and this page will explain how to avoid these leakages.

     

    Definitions

    WeakReferences and SoftReferences

     

    Before talking about common problems on caching Classes (and reflection objects), lets take a look on WeakReferences and SoftReferences.

    They are in a first look very similar.

    They are both cleared only if all the references to the underlying object are cleared. (we will talk about their differences later).

     

    For example, on this code:

    testCase1() throws Exception
    {
        Object x = new Object();
        WeakReference reference = new WeakReference(x);
        x = null; /// reference.get() will be null only if this reference is cleared first
        System.gc();
        Thread.sleep(1000); // just to guarantee a GC
        assertNull(reference.get());
    }
    

    reference.get() will be null only after x was set to null, and after a GC operation.

     

     

    SoftReferences

    SoftReferences behave exactly as WeakReferences with the difference that they are cleared only when the JVM needs more memory. (in other words, only when the JVM feels like).

     

    So, if you want to create a testcase to validate SoftReferences you should do something like:

     

    testCase1() throws Exception
    {
        Object x = new Object();
        SoftReference reference = new SoftReference(x);
        x = null; /// reference.get() will be null only if this reference is cleared first
        forceOutOfMemory();
        System.gc();
        Thread.sleep(1000); // just to guarantee a GC
        assertNull(reference.get());
    }
    
    public void forceoutOfMemory()
    {
       ArrayList<String> list = new ArrayList<String>();
       try
       {
           for (int i=0; ; i++)
           { 
                list.add("A big stringA big stringA big stringA big string" + i);
           }
       }
       catch (Throwable e)
       {
       }
       list.clear();
    }
    

     

    So, the same reference will be cleared when x is nulled and a GC happen and the JVM needs more memory.

     

     

    WeakHashMap

    WeakHashMap is a very usefull tool for ClassLoader caching. It's very common to create a WeakHashMap indexed by ClassLoader with another HashMap by String/Class (or its MetaData).

     

    On jboss-commons there are some variants that could be useful:

     

    - SoftValueHashMap (It keeps the value as a SoftRefrence)

    - WeakValueHashMap (It keeps the value as a WeakReference).

     

    These are different from WeakHashMap as they keep the value with Soft/Weak reference.

     

    Common Problems

     

    This section will explain common problems you could have while keeping reflection/classes on cache.

     

    Circular References to ClassLoader

    • java.lang.Class will of course have a reference to ClassLoader. So, if you want to allow redeployments any cache to Class need to be done through WeakReference (or WeakValueHashMap or anything similar).

    • java.lang.reflect.Method or any reflection object also has a reference to Class->ClassLoader what will prevent redeployments. There will be a section on this page talking specifically about reflectin objects, as there are extra considerations.

     

    static references to Objects

    Singletons won't prevent redeployments. If your class has a static field holding a reference to an object, your class still can be GCed as when all references to the classLoader/class are gone the static field is also gone.

     

    But if you hold static references in a cache that's a problem.

     

    Reference is too weak

     

    For objects like java.lang.Class/ClassLoader you could rely on WeakReferences as you have the JVM managing the life cycle of the object Class and ClassLoader.

    But for your own objects this might represent an issue.

     

    For example, in case you have a MetaData class associated with class, you can't use a WeakReference to your MetaData as any GC will clear your reference. Unless you are doing some special treatment.

     

        class MetaData
        {
        }
    
        WeakReference reference = new WeakReference(new ClassMetaData()); // this reference won't last for a long time.
    

     

     

    You will also have this problem on any reflection object, as when you get the reflect method from the JVM, you are in fact using a copy.

     

        WeakReference referenceToMethod = new WeakReference(someClass.getMethod(...));   // this reference won't last a long time either.
    

     

     

    The solution for too weak reference might be to use SoftReferences, or to use a different algorithm where you don't need a WeakReference to your Object. (Well, you have that option unless your object is a reflection object).

     

     

    Checks for NullPointerException

     

    Every time you use a reflection object over a SoftReference, always make sure the object still exist (checking for null reference). In case you get a null reference you will have to recreate the reference. (Use a synchronized block on that case).

     

     

    Final recomendations

     

    TestCases

    You could create a testcase validating the behavior under classLoader conditions.

    JBossSerialization has an example:

     

    Using JVMTIInterface from JBossProfiler you could do things like test for loaded Classes, and even look for refrenceHolder by JVMTIInterface API. The example bellow is using that technique.

     

    http://fisheye.jboss.com/viewrep/~raw,r=1.8/JBoss/jboss-serialization/tests/org/jboss/serial/memory/MemoryLeakTestCase.java

     

    We will be creating some infra structure for memory leak testcases on JBAS:

     

    http://jira.jboss.org/jira/browse/JBAS-2909

     

    Summary

    • Strong references to reflection objects will prevent class from being Garbage Collected

    • If you need to reference reflection objects, use SoftReference (and use double check to avoid NPE)

    • An example of a structure you could use to avoid Classleakages:

      • WeakHashMap<ClassLoader,ConcurrentHashMap<String,ClassMetaData>>