diff options
author | aclement <aclement> | 2006-08-08 11:26:28 +0000 |
---|---|---|
committer | aclement <aclement> | 2006-08-08 11:26:28 +0000 |
commit | 387c3ac6f23a39aebbc4044093c793009dfea8f6 (patch) | |
tree | d1e7d5bfb3796df01bc28fbf6678e70a8026c284 /bcel-builder | |
parent | 4f3c86ded09ce6407d51ab6ca98fac6ed16f8e59 (diff) | |
download | aspectj-387c3ac6f23a39aebbc4044093c793009dfea8f6.tar.gz aspectj-387c3ac6f23a39aebbc4044093c793009dfea8f6.zip |
152979: shared cache in the repositorypre_pr_153572
Diffstat (limited to 'bcel-builder')
3 files changed, 214 insertions, 32 deletions
diff --git a/bcel-builder/src/org/aspectj/apache/bcel/util/ClassLoaderRepository.java b/bcel-builder/src/org/aspectj/apache/bcel/util/ClassLoaderRepository.java index 93c81f20d..1c1f0c359 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/util/ClassLoaderRepository.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/util/ClassLoaderRepository.java @@ -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(); } } diff --git a/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/AllTests.java b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/AllTests.java index 87ed85774..da406e725 100644 --- a/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/AllTests.java +++ b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/AllTests.java @@ -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 index 000000000..d9e0d6d58 --- /dev/null +++ b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/ClassloaderRepositoryTest.java @@ -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(); + } + +} |