1 Reply Latest reply on May 15, 2007 6:40 PM by jason.greene

    ModificationEntry memory leak when aspectized object used in

    tfila

      Hello,


      I believe that there is a memory leak in the PojoCache (JBossCache-1.4.1.SP3) when storing aspectized objects in a collection in the cache. ModifcationEntry objects are added to the PojoCache undoListLocal_ ThreadLocal object, but are never removed after a successful commit to the cache and contain references to the objects being placed in the cache. The undoListLocal_ list continues to grow while the same thread is used to make modifications to the cache.

      Below is a unit test which shows this behavior.


      package org.jboss.cache.aop.collection;
      
       import java.io.Serializable;
       import junit.framework.Test;
       import junit.framework.TestCase;
       import junit.framework.TestSuite;
       import org.apache.commons.logging.Log;
       import org.apache.commons.logging.LogFactory;
       import org.jboss.cache.aop.PojoCache;
       import org.jboss.cache.PropertyConfigurator;
      
       import java.util.*;
      
       import javax.transaction.TransactionManager;
       import javax.transaction.Transaction;
      
      
       class MyReplicatedClass
       {
       String key = null;
       int value;
      
       public MyReplicatedClass( String key, int value )
       {
       this.key = key;
       this.value = value;
       }
       }
      
       class MySerializableReplicatedClass implements Serializable
       {
       private static final long serialVersionUID = 6049469200632947143L;
      
       String key = null;
       int value;
      
       public MySerializableReplicatedClass( String key, int value )
       {
       this.key = key;
       this.value = value;
       }
       }
      
       public class ReplicatedAsyncCollectionTest extends TestCase
       {
       Log log=LogFactory.getLog(ReplicatedAsyncCollectionTest.class);
       PojoCache cache1;
       PojoCache cache2;
      
       public ReplicatedAsyncCollectionTest(String name)
       {
       super(name);
       }
      
       protected void setUp() throws Exception
       {
       super.setUp();
       log.info("setUp() ....");
       cache1 = createAsyncCache("CacheGroup");
       cache2 = createAsyncCache("CacheGroup");
       }
      
       protected void tearDown() throws Exception
       {
       super.tearDown();
       cache1.remove("/");
       cache1.stop();
       cache2.stop();
       }
      
       private PojoCache createAsyncCache(String name) throws Exception {
       PojoCache tree=new PojoCache();
       PropertyConfigurator config=new PropertyConfigurator();
       config.configure(tree, "META-INF/replAsync-service.xml");
      
       tree.setClusterName(name);
       tree.createService();
       tree.startService();
       return tree;
       }
      
       public void testPojoMapModList() throws Exception
       {
       Map myMap = new HashMap();
       cache1.putObject( "/myTest", myMap );
       myMap = (HashMap)cache1.getObject("/myTest");
       TransactionManager mgr = cache1.getTransactionManager();
       for ( int i = 0; i < 10; i++ )
       {
       mgr.begin();
      
       MySerializableReplicatedClass mrc = new MySerializableReplicatedClass(Integer.valueOf(i).toString(), i );
       myMap.put(mrc.key, mrc);
      
       mgr.commit();
       }
      
       log.info( "cache1 modification list size = "
       + (cache1.getModList() == null ? 0 : cache1.getModList().size()) );
      
       assertEquals(cache1.getModList().size(), 0 );
       }
      
       public void testPojoAopMapModList() throws Exception
       {
       Map myMap = new HashMap();
       cache1.putObject( "/myTest", myMap );
       myMap = (HashMap)cache1.getObject("/myTest");
       TransactionManager mgr = cache1.getTransactionManager();
       for ( int i = 0; i < 10; i++ )
       {
       mgr.begin();
      
       MyReplicatedClass mrc = new MyReplicatedClass(Integer.valueOf(i).toString(), i );
      
       myMap.put(mrc.key, mrc);
      
       mgr.commit();
      
       }
       // Possible work around to manually clear the undo list
       // cache1.getModList().clear();
       log.info( "cache1 modification list size = "
       + (cache1.getModList() == null ? 0 : cache1.getModList().size()) );
      
       assertEquals(cache1.getModList().size(), 0 );
       }
      
      
       public static Test suite() throws Exception
       {
       return new TestSuite(ReplicatedAsyncCollectionTest.class);
       }
      
       public static void main(String[] args) throws Exception
       {
       junit.textui.TestRunner.run(suite());
       }
      
       }
      
      


      The corresponding jboss-aop.xml is

      <?xml version="1.0" encoding="UTF-8"?>
      
       <aop>
      
       <prepare expr="field(* org.jboss.cache.aop.collection.MyReplicatedClass->*)" />
      
       </aop>
      


      The META-INF/replAsync-service.xml that comes with the JBossCache tests can be used.

      The unit test contains two tests: the first (testPojoMapModList) adds Serializable objects to the collection in the PojoCache; and the other (testPojoAopMapModList) adds AOP'd objects to the collection. In the first test after adding Serializable objects to the collection, the undoListLocal_ object is clean. In the second test after adding the AOP'd objects to the collection, the undoListLocal_ still contains all ModificationEntry objects from the transactions used to update the collection. This behavior is also seen is earlier versions of the PojoCache.

      The ModificationEntry (PojoCache.undoListLocal_) list can be retrieved from the PojoCache through the getModList() method for the executing thread. Is it safe to clear this list manually after the transaction has been completed? If so this would be a usable solution for this version. The other solution is to use non-AOP'd (i.e. Serializable objects) in the collection, though that is not optimal.

      Any thoughts or other work around would be appreciated.

      Thanks.