]> source.dussan.org Git - aspectj.git/commitdiff
152979: shared cache in the repository pre_pr_153572
authoraclement <aclement>
Tue, 8 Aug 2006 11:26:28 +0000 (11:26 +0000)
committeraclement <aclement>
Tue, 8 Aug 2006 11:26:28 +0000 (11:26 +0000)
bcel-builder/src/org/aspectj/apache/bcel/util/ClassLoaderRepository.java
bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/AllTests.java
bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/ClassloaderRepositoryTest.java [new file with mode: 0644]
lib/bcel/bcel-src.zip
lib/bcel/bcel.jar

index 93c81f20dcb49b7523d5f5ac33560b1e577b7555..1c1f0c3595944f96dae6361bbb4147d7a5700ea4 100644 (file)
@@ -56,6 +56,11 @@ package org.aspectj.apache.bcel.util;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.WeakHashMap;
 
 import org.aspectj.apache.bcel.classfile.ClassParser;
@@ -70,86 +75,195 @@ import org.aspectj.apache.bcel.classfile.JavaClass;
  *
  * @see org.aspectj.apache.bcel.Repository
  *
- * @version $Id: ClassLoaderRepository.java,v 1.5 2006/03/10 13:29:05 aclement Exp $
+ * @version $Id: ClassLoaderRepository.java,v 1.6 2006/08/08 11:26:28 aclement Exp $
  * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
  * @author David Dixon-Peugh
  */
-public class ClassLoaderRepository
-  implements Repository
-{
+public class ClassLoaderRepository implements Repository {
   private java.lang.ClassLoader loader;
-  private WeakHashMap loadedClasses =
-    new WeakHashMap(); // CLASSNAME X JAVACLASS
+  private WeakHashMap /*<String classname,JavaClass>*/loadedClassesLocalCache = new WeakHashMap(); 
+  private static Map /*<URL,JavaClass>*/loadedUrlsSharedCache = new HashMap(); 
+  public static boolean useSharedCache = true;
+  
+  private static long timeManipulatingURLs = 0L; 
+  private static long timeSpentLoading     = 0L;
+  private static int  classesLoadedCount = 0;
+  private static int  cacheHitsShared    = 0;
+  private static int  missSharedEvicted  = 0; // Misses in shared cache access due to reference GC
+  private static int  misses             = 0;
+  private int  cacheHitsLocal     = 0;
+  private int  missLocalEvicted   = 0; // Misses in local cache access due to reference GC
 
+  static {
+    useSharedCache = System.getProperty("org.aspectj.apache.bcel.useSharedCache","true").equalsIgnoreCase("true");
+  }
+  
   public ClassLoaderRepository( java.lang.ClassLoader loader ) {
     this.loader = loader;
   }
 
+  /**
+   * Store a new JavaClass into this repository as a soft reference and return the reference
+   */
+  private Reference storeClassAsReference( JavaClass clazz ) {
+       Reference ref = new SoftReference(clazz);
+    loadedClassesLocalCache.put( clazz.getClassName(), ref);                  
+    clazz.setRepository( this );
+    return ref;
+  }
+  
+  /**
+   * Store a reference in the shared cache
+   */
+  private void storeReferenceShared(URL url, Reference ref) {
+         if (useSharedCache) loadedUrlsSharedCache.put(url, ref);
+  }
+
   /**
    * Store a new JavaClass into this Repository.
    */
   public void storeClass( JavaClass clazz ) {
-    loadedClasses.put( clazz.getClassName(),
-                      clazz );
-    clazz.setRepository( this );
+         storeClassAsReference(clazz);
   }
 
   /**
    * Remove class from repository
    */
   public void removeClass(JavaClass clazz) {
-    loadedClasses.remove(clazz.getClassName());
+    loadedClassesLocalCache.remove(clazz.getClassName());
   }
 
   /**
-   * Find an already defined JavaClass.
+   * Find an already defined JavaClass in the local cache.
    */
   public JavaClass findClass( String className ) {
-    if ( loadedClasses.containsKey( className )) {
-      return (JavaClass) loadedClasses.get( className );
-    } else {
-      return null;
+    Object o = loadedClassesLocalCache.get( className );
+    if (o != null) {
+       o = ((Reference)o).get();
+       if (o != null) {
+               return (JavaClass)o;
+       } else {
+               missLocalEvicted++;
+       }
     }
+    return null;
+  }
+  
+  /**
+   * Find an already defined JavaClass in the shared cache.
+   */
+  private JavaClass findClassShared(URL url) {
+         if (!useSharedCache) return null;
+         Object o = (Reference)loadedUrlsSharedCache.get(url);
+         if (o != null) {
+               o = ((Reference)o).get();
+               if (o != null) {
+                       return (JavaClass)o; 
+               } else { 
+                       missSharedEvicted++; 
+               }
+         }
+         return null;
   }
 
+  
   /**
    * Lookup a JavaClass object from the Class Name provided.
    */
-  public JavaClass loadClass( String className ) 
-    throws ClassNotFoundException
-  {
+  public JavaClass loadClass( String className ) throws ClassNotFoundException {
     String classFile = className.replace('.', '/');
 
-    JavaClass RC = findClass( className );
-    if (RC != null) { return RC; }
+    // Check the local cache
+    JavaClass clazz = findClass(className);
+    if (clazz != null) { cacheHitsLocal++; return clazz; }
 
     try {
-      InputStream is = 
-       loader.getResourceAsStream( classFile + ".class" );
+       // Work out the URL
+       long time = System.currentTimeMillis();
+       java.net.URL url = (useSharedCache?loader.getResource( classFile + ".class" ):null);
+       if (useSharedCache && url==null) throw new ClassNotFoundException(className + " not found.");
+               InputStream is = (useSharedCache?url.openStream():loader.getResourceAsStream( classFile + ".class" ));
+               timeManipulatingURLs += (System.currentTimeMillis() - time);
+               
+               // Check the shared cache
+               clazz = findClassShared(url);
+               if (clazz != null) { cacheHitsShared++; timeSpentLoading+=(System.currentTimeMillis() - time); return clazz; } 
+
+               // Didn't find it in either cache
+               misses++;
            
-      if(is == null) {
-       throw new ClassNotFoundException(className + " not found.");
-      }
+        if (is == null) { // TODO move this up?
+         throw new ClassNotFoundException(className + " not found.");
+        }
 
-      ClassParser parser = new ClassParser( is, className );
-      RC = parser.parse();
+        ClassParser parser = new ClassParser( is, className );
+        clazz = parser.parse();
            
-      storeClass( RC );
+        // Store it in both caches
+        Reference ref = storeClassAsReference( clazz );
+        storeReferenceShared(url,ref);
 
-      return RC;
+        timeSpentLoading += (System.currentTimeMillis() - time);
+           classesLoadedCount++;
+        return clazz;
     } catch (IOException e) {
       throw new ClassNotFoundException( e.toString() );
     }
   }
+  
 
+/**
+   * Produce a report on cache usage.
+   */
+  public String reportAllStatistics() {
+         StringBuffer sb = new StringBuffer();
+         sb.append("BCEL repository report.");
+         if (!useSharedCache) sb.append(" (Shared cache deactivated)");
+         sb.append(" Total time spent loading: "+timeSpentLoading+"ms.");
+         sb.append(" Time manipulating URLs: "+timeManipulatingURLs+"ms.");
+         sb.append(" Classes loaded: "+classesLoadedCount+".");
+         if (useSharedCache) sb.append(" URL cache (hits/missDueToEviction): ("+cacheHitsShared+"/"+missSharedEvicted+").");
+         sb.append(" Local cache (hits/missDueToEviction): ("+cacheHitsLocal+"/"+missLocalEvicted+").");
+         return sb.toString();
+  }
+  
+  public int reportLocalCacheHits() {
+         return cacheHitsLocal;
+  }
+
+  public static int reportSharedCacheHits() {
+         return cacheHitsShared;
+  }
+  
+  /**
+   * Reset statistics and clear all caches
+   */
+  public void reset() {
+         timeManipulatingURLs = 0L; 
+         timeSpentLoading = 0L;
+         classesLoadedCount = 0;
+         cacheHitsLocal    = 0;
+         cacheHitsShared   = 0;
+         missSharedEvicted = 0; 
+         missLocalEvicted  = 0; 
+         misses = 0;
+         clear();
+         clearShared();
+  }
+  
+  
   public JavaClass loadClass(Class clazz) throws ClassNotFoundException {
     return loadClass(clazz.getName());
   }
 
-  /** Clear all entries from cache.
-   */
+  /** Clear all entries from the local cache */
   public void clear() {
-    loadedClasses.clear();
+      loadedClassesLocalCache.clear();
+  }
+
+  /** Clear all entries from the shared cache */
+  public static void clearShared() {
+         loadedUrlsSharedCache.clear();
   }
 }
 
index 87ed8577477fe1a663121c7c266e2d894dfb8aef..da406e725bbef3bab4fb549a8a6cd954900d1d76 100644 (file)
@@ -38,6 +38,7 @@ public class AllTests {
                suite.addTestSuite(EnclosingMethodAttributeTest.class);
                suite.addTestSuite(MethodAnnotationsTest.class);
                suite.addTestSuite(RuntimeVisibleAnnotationAttributeTest.class);
+               suite.addTestSuite(ClassloaderRepositoryTest.class);
                suite.addTestSuite(EnumAccessFlagTest.class);
                suite.addTestSuite(LocalVariableTypeTableTest.class);
                suite.addTestSuite(VarargsTest.class);
diff --git a/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/ClassloaderRepositoryTest.java b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/ClassloaderRepositoryTest.java
new file mode 100644 (file)
index 0000000..d9e0d6d
--- /dev/null
@@ -0,0 +1,67 @@
+package org.aspectj.apache.bcel.classfile.tests;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import org.aspectj.apache.bcel.classfile.JavaClass;
+import org.aspectj.apache.bcel.util.ClassLoaderRepository;
+
+import junit.framework.TestCase;
+
+/*
+ * Tests create a simple classloader repository configuration and check sharing of information.
+ */
+public class ClassloaderRepositoryTest extends TestCase {
+
+       private ClassLoaderRepository rep1,rep2;
+       
+       public void setUp() throws Exception {
+               super.setUp();
+               ClassLoader cl = Thread.currentThread().getContextClassLoader();
+               ClassLoader cl1 = new URLClassLoader(new URL[]{},cl);
+               ClassLoader cl2 = new URLClassLoader(new URL[]{},cl);
+               rep1 = new ClassLoaderRepository(cl1);
+               rep2 = new ClassLoaderRepository(cl2);
+       }
+
+       // Retrieve string 5 times from same repository, 4 hits should be from local cache
+       public void testLocalCacheWorks() throws ClassNotFoundException {               
+               JavaClass jc = rep1.loadClass("java.lang.String");
+               jc = rep1.loadClass("java.lang.String");
+               jc = rep1.loadClass("java.lang.String");
+               jc = rep1.loadClass("java.lang.String");
+               jc = rep1.loadClass("java.lang.String");
+               assertTrue("Should have used local cache 4 times: "+rep1.reportLocalCacheHits(),rep1.reportLocalCacheHits()==4);
+       }
+
+       // Retrieve String through one repository then load again through another, should be shared cache hit
+       public void testSharedCacheWorks() throws ClassNotFoundException {              
+               JavaClass jc = rep1.loadClass("java.lang.String");
+               jc = rep2.loadClass("java.lang.String");
+               assertTrue("Should have retrieved String from shared cache: "+ClassLoaderRepository.reportSharedCacheHits(),
+                               ClassLoaderRepository.reportSharedCacheHits()==1);
+       }
+       
+       // Shared cache OFF, shouldn't get a shared cache hit
+       public void testSharedCacheCanBeDeactivated() throws ClassNotFoundException {           
+               try {
+                       ClassLoaderRepository.useSharedCache=false;
+                       JavaClass jc = rep1.loadClass("java.lang.String");
+                       jc = rep2.loadClass("java.lang.String");
+                       assertTrue("Should not have retrieved String from shared cache: "+
+                                       ClassLoaderRepository.reportSharedCacheHits(),
+                                   ClassLoaderRepository.reportSharedCacheHits()==0);
+               } finally {
+                       ClassLoaderRepository.useSharedCache=true;
+               }
+       }
+       
+       public void tearDown() throws Exception {
+               super.tearDown();
+               System.err.println("Rep1: "+rep1.reportAllStatistics());
+               System.err.println("Rep2: "+rep2.reportAllStatistics());
+               rep1.reset();
+               rep2.reset();
+       }
+       
+}
index f8dc6805c3ecc69e34c3d2a7ceba8f1a59749984..bf7670c8c46d70db9429d01325213824c59aa672 100644 (file)
Binary files a/lib/bcel/bcel-src.zip and b/lib/bcel/bcel-src.zip differ
index 6125439b80783818249e7bb6b698977c4e04f825..5bcbec16bbc1ea78d42c7ece19438ed025581e05 100644 (file)
Binary files a/lib/bcel/bcel.jar and b/lib/bcel/bcel.jar differ