13 Replies Latest reply on Mar 17, 2006 5:11 PM by Clebert Suconic

    SoftReferences and WeakReferences

    Clebert Suconic Master

      Anyone know in detail when a SoftReference is supposed to be cleared?

      I'm creating a testcase, using jbossProfiler interface, to validate classUnloading in a javaSerialization, and the class is not being unloaded when I expect.

      the reason why I'm testing JavaSerialization, is because I was having problems with SoftReferences on JBossSerialization, and I wanted to validate the behavior of these references on a regular JavaSerialization.

      There are still references on a SoftCache somewhere:

      This is the testcase...

       public void testJavaSerialization() throws Exception
       {
       JVMTIInterface jvmtiInterface = new JVMTIInterface();
       try
       {
       serializeJava(jvmtiInterface);
      
       ClassMetamodelFactory.clear();
       ArrayList newList = new ArrayList();
      
       // forcing a OutOfMemoryError, just to see if the SoftCache is cleared as stated on SoftCache javadoc
       try
       {
       int count=0;
       while (true)
       {
       newList.add("sdflkjsdlf jsflkja dflaj df;lkjas dfokjas dfkajs dfakjsdf sdfklj sdflkjs dflksjd flskjdf slkdjf slkdfj sldkfj sldfkj sfdlkj oadsjf oaidjf aosidjf aosidjf aosdijf aoidsfj aosidjf " + (count++));
       }
       }
       catch (OutOfMemoryError e)
       {
      
       System.out.println("Clearing newList");
       jvmtiInterface.forceGC();
       newList.clear();
       jvmtiInterface.forceGC();
       }
      
       Class clazz = jvmtiInterface.getClassByName(CLASS_NAME);
       if (clazz!=null)
       {
       Object obj = clazz;
       Object[] referenceHolders = printReferences(jvmtiInterface, obj);
      
       // You can use JBossProfiler to analyze this snapshot
       jvmtiInterface.heapSnapshot("./memory-tst","mem");
       }
       assertNull("Class org.jboss.serial.memory.SomePojo should been unloaded already",clazz);
      
       }
       catch (Exception e)
       {
       e.printStackTrace();
       Class clazz = jvmtiInterface.getClassByName(CLASS_NAME);
       if (clazz!=null)
       {
       Object obj = clazz;
       Object[] referenceHolders = printReferences(jvmtiInterface, obj);
      
       // You can use JBossProfiler to analyze this snapshot
       jvmtiInterface.heapSnapshot("./memory-ex2","mem");
       }
       System.out.println("Class org.jboss.serial.memory.SomePojo should been unloaded already" + clazz);
       throw e;
       }
       }
      
      



      And this is the serializeJava method:
      Notice that if I comment out writeObject, the class is cleared from the Heap
       private void serializeJava(JVMTIInterface jvmtiInterface) throws Exception {
      
       Thread newThread = new Thread()
       {
       public void run() {
       try
       {
       URLClassLoader loader = newClassLoader();
      
       Class testClass = loader.loadClass(CLASS_NAME);
       Object someInstance = testClass.newInstance();
      
       ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
       ObjectOutputStream objOut = new ObjectOutputStream(byteOut);
      
       objOut.writeObject(someInstance);
       objOut.flush();
      
       loader=null;
       testClass=null;
       someInstance=null;
       objOut=null;
       }
       catch (Exception e)
       {
       e.printStackTrace();
       }
       }
       };
      
       newThread.start();
       newThread.join();
       }
      
      




      I'm getting this output, for objects holding references:

      References for class org.jboss.serial.memory.test.SomePojo
      Reference[0]=[Ljava.lang.Object;
      Reference[1]=java.util.HashMap$Entry

        • 2. Re: SoftReferences and WeakReferences
          Clebert Suconic Master

          I created another testcase playing with WeakReference and SoftReference.

          The only way to clear a SoftReference, is by forcing a OutOfMemoyError.
          JavaSerialization uses SoftReferences:

          package org.jboss.serial.memory;
          
          import java.lang.ref.SoftReference;
          import java.lang.ref.WeakReference;
          import java.util.ArrayList;
          
          import org.jboss.profiler.jvmti.JVMTIInterface;
          import org.jboss.serial.memory.test.SomePojo;
          
          import junit.framework.TestCase;
          
          public class SoftReferenceTest extends TestCase
          {
          
           public void testSoftReference()
           {
           SoftReference softReference = new SoftReference(new SomePojo());
           assertNotNull(softReference.get());
           JVMTIInterface jvmtiInterface = new JVMTIInterface();
          
           forceOutOfMemory();
          
           jvmtiInterface.forceGC();
           assertNull(softReference.get());
           }
          
           public void testWeakReference()
           {
           Object somePojo = new SomePojo();
          
           WeakReference softReference = new WeakReference(somePojo );
           assertNotNull(softReference.get());
           JVMTIInterface jvmtiInterface = new JVMTIInterface();
           jvmtiInterface.forceGC();
           assertNotNull(softReference.get());
           somePojo=null;
           jvmtiInterface.forceGC();
           assertNull(softReference.get());
           }
          
           private void forceOutOfMemory() {
           ArrayList list = new ArrayList();
           try
           {
           int counter=0;
           while (true)
           {
           list.add("A big String A big String A big String A big String A big String A big String A big String " + (counter++));
           }
           }
           catch (OutOfMemoryError e)
           {
           list.clear();
           }
           }
          }
          


          • 3. Re: SoftReferences and WeakReferences
            Anil Saldanha Master

            Clebert, here is the take on SoftReferences. They are not GCed until the system memory is running very very low. Then there is no guarantee that they will be GCed.

            But you are right, throwing a OOM error, will clear the SoftReferences.

            • 4. Re: SoftReferences and WeakReferences
              Clebert Suconic Master

              Let me make this post just to make a record on what I found:


              I - SoftReferences are cleared when you don't have any other reference to the referenced object, and the JVM is about to ask the OS for more memory. (or before an OutOfMemoryException).

              II - It's not efficient to make WeakReferences on any reflection object. java.lang.Class is creating a copy when you do a getField, getMethod or similar operations. That means your WeakReference is the only reference to the underlying object what means any minor GC will clear it.

              For being efficient with Reflection Objects and WeakReferences you should be using SoftReferences and doing extra check to rebuild the object in case you loose the reference (in other words when the JVM needs more memory).

              • 5. Re: SoftReferences and WeakReferences
                Scott Stark Master

                It would help if you describe the full context of why references are being used at all here then because your description of the WeakReferences issue makes me wonder why there just aren't hard references via a regular map being used, and clearing that map at the end of whatever operation is involved. The behavior of the WeakReferences suggests that this is a well defined operation with a begin/end with no sharing of object outside of the operation scope so I'm wondering why references are needed.

                You should create a references practices wiki page to summarize the issues you have found.

                • 6. Re: SoftReferences and WeakReferences
                  Clebert Suconic Master

                  There are two main MetaData classes. ClassMetaData, and ClassMetaDataField.

                  ClassMetaData has references to Constructor and Methods.
                  ClassMetaDataField has references to Fields.

                  I need these objects the entire life cycle of ClassMetaData. In other words, I need to get rid of them when the ClassLoader goes away, and that was the tricky part.

                  If I kept hard references, the ClassLoader won't go away as java.lang.reflect.Field or Method has a reference to Class internally.

                  If I kept WeakReferences, these Methods were being cleared sooner than I expected.

                  It would be too expensive to get these objects every time from reflection, so I needed a cache.

                  As the begin/end is the entire life cycle of the ClassLoader, I had to play with SoftReferences.

                  I will write a WikiPage.

                  • 7. Re: SoftReferences and WeakReferences
                    Clebert Suconic Master

                    Just another point:

                    During my "research" I looked on how MarshalledInvocation (from JBossAS) was keeping references.

                    MarshalledInvocation is keeping hard references, but they are kept into the MBean. MarshalledInvocation has a method setHash(methods).
                    That method is called and cleared on every invocation, and that HashMap is released on an undeployment event.

                    As Serialization is more generic than MBean invocations, I couldn't use such undeployment events.

                    • 8. Re: SoftReferences and WeakReferences
                      Scott Stark Master

                      ClassLoaders (aka the root of most problems in java).

                      So it seems like WeakReferences really are the correct type of reference, but the circular reference between reflection objects, their Class, and ClassLoader makes it hard to create a proper structure that allows the metadata to be tied to the ClassLoader via a WeakReference?

                      WeakHashMap(ClassLoader, Set(ClassMetaData)) won't work because this produces a strong reference back to the WeakHashMap key. Can you come up with a data structure that does work with WeakReferences to the ClassLoader?

                      • 9. Re: SoftReferences and WeakReferences
                        Clebert Suconic Master

                         

                        "Scott Stark" wrote:
                        So it seems like WeakReferences really are the correct type of reference, but the circular reference between reflection objects, their Class, and ClassLoader makes it hard to create a proper structure that allows the metadata to be tied to the ClassLoader via a WeakReference?


                        Yes, if ClassMetaData is holding a reference to Method, Method is holding a reference to Class/ClassLoader, so the element is not cleared from the WeakHashMap.

                        "Scott Start" wrote:
                        WeakHashMap(ClassLoader, Set(ClassMetaData)) won't work because this produces a strong reference back to the WeakHashMap key. Can you come up with a data structure that does work with WeakReferences to the ClassLoader?


                        Only if I could use WeakReference for places where I'm using Reflection Objects. But that is causing WeakReference being cleared every time (causing performance problems).

                        So, what I'm doing is WeakHashMap<ClassLoader,Set>

                        And on ClassMetaData
                        SoftReference method;

                        and doing extra checks for (if method.get()==null) rebuild it. (I have actually encapsulated such behavior in a inner class).


                        usually on redeployments the JVM will need more memory for the new JAR being loaded, what means SoftReferences being cleared.

                        JavaSerialization also uses SoftReferences for some AOP Like structures (byte weaved reflection methods) and reflection objects. (So, we already have such SoftReference behavior on the JVM)

                        • 10. Re: SoftReferences and WeakReferences
                          Clebert Suconic Master

                           

                          "Clebert" wrote:
                          But that is causing WeakReference being cleared every time (causing performance problems).


                          Of course WeakReference caused a NullPointerException at first. I had performance problems when I decided to recreate reflection objects every time reference.get()==null. That's why I decided by SoftReferences.

                          • 11. Re: SoftReferences and WeakReferences
                            Adrian Brock Master

                            SoftReferences are cleared when the JDK feels like it.
                            Usually because memory is short.

                            If you look at Sun's docs, it depends whether the JDK is in
                            server or client mode.

                            Why don't you just use the
                            org.jboss.util.SoftValueHashMap

                            I believe Hibernate has a SoftLimitMRUCache
                            which sounds like it also flushes soft references that haven't been used
                            for a while.

                            • 12. Re: SoftReferences and WeakReferences
                              Clebert Suconic Master

                              I would still have the problem on ClassMetaData holding a strong reference to java.lang.reflection.Method,Field or Constructor.

                              SoftReferences are not cleared if something else is holding a strong reference. (The same way as WeakHashMap).

                              What I needed was to keep these reflection objects in a Soft/WeakReference.


                              I validated these scenarios on testSerialization and testMetadata at this testcase:

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

                              • 13. Re: SoftReferences and WeakReferences
                                Clebert Suconic Master

                                I have produced a wiki page for this:

                                http://wiki.jboss.org/wiki/Wiki.jsp?page=ClassLeakage

                                I don't consider myself a good writer, so if someone else could read it and fix any issues.