diff options
author | aclement <aclement> | 2006-08-21 15:23:58 +0000 |
---|---|---|
committer | aclement <aclement> | 2006-08-21 15:23:58 +0000 |
commit | 0185a0214f790c6611b48b986e01ef97a399b6ae (patch) | |
tree | 9f7c1fa2002c7d29c08ef81da63e314ffe63f360 /bcel-builder | |
parent | bec92132ab1ee779bc910c62fd8cb854973d06cf (diff) | |
download | aspectj-0185a0214f790c6611b48b986e01ef97a399b6ae.tar.gz aspectj-0185a0214f790c6611b48b986e01ef97a399b6ae.zip |
some updates to ClassLoaderRepository - tested by RontimeWeaving
Diffstat (limited to 'bcel-builder')
-rw-r--r-- | bcel-builder/src/org/aspectj/apache/bcel/util/ClassLoaderRepository.java | 258 | ||||
-rw-r--r-- | bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/ClassloaderRepositoryTest.java | 38 |
2 files changed, 204 insertions, 92 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 6c30dade8..2f7325186 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/util/ClassLoaderRepository.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/util/ClassLoaderRepository.java @@ -57,11 +57,15 @@ package org.aspectj.apache.bcel.util; import java.io.IOException; import java.io.InputStream; import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.net.URL; import java.net.URLClassLoader; +import java.util.AbstractMap; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.WeakHashMap; import org.aspectj.apache.bcel.classfile.ClassParser; @@ -76,32 +80,35 @@ import org.aspectj.apache.bcel.classfile.JavaClass; * * @see org.aspectj.apache.bcel.Repository * - * @version $Id: ClassLoaderRepository.java,v 1.7 2006/08/18 14:51:00 acolyer Exp $ + * @version $Id: ClassLoaderRepository.java,v 1.8 2006/08/21 15:23:58 aclement Exp $ * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> * @author David Dixon-Peugh */ public class ClassLoaderRepository implements Repository { private static java.lang.ClassLoader bootClassLoader = null; private java.lang.ClassLoader loader; - 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; + // Choice of cache... + private WeakHashMap /*<URL,SoftRef(JavaClass)>*/localCache = new WeakHashMap(); + private static SoftHashMap /*<URL,JavaClass>*/sharedCache = new SoftHashMap(Collections.synchronizedMap(new HashMap())); + + // For fast translation of the classname *intentionally not static* + private SoftHashMap /*<String,URL>*/ nameMap = new SoftHashMap(new HashMap(), false); + + public static boolean useSharedCache = + System.getProperty("org.aspectj.apache.bcel.useSharedCache","true").equalsIgnoreCase("true"); + 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 long timeManipulatingURLs = 0L; + private long timeSpentLoading = 0L; + private int classesLoadedCount = 0; + private 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 != null) ? loader : getBootClassLoader(); + this.loader = (loader != null) ? loader : getBootClassLoader(); } private static synchronized java.lang.ClassLoader getBootClassLoader() { @@ -110,43 +117,113 @@ public class ClassLoaderRepository implements Repository { } return bootClassLoader; } + + // Can track back to its key + public static class SoftHashMap extends AbstractMap { + private Map map; + boolean recordMiss = true; // only interested in recording miss stats sometimes + private ReferenceQueue rq = new ReferenceQueue(); + + public SoftHashMap(Map map) { this.map = map; } + public SoftHashMap() { this(new HashMap()); } + public SoftHashMap(Map map, boolean b) { this(map); this.recordMiss=b;} + + class SpecialValue extends SoftReference { + private final Object key; + SpecialValue(Object k,Object v) { + super(v,rq); + this.key = k; + } + } + + private void processQueue() { + SpecialValue sv = null; + while ((sv = (SpecialValue)rq.poll())!=null) { + map.remove(sv.key); + } + } + + public Object get(Object key) { + SpecialValue value = (SpecialValue)map.get(key); + if (value==null) return null; + if (value.get()==null) { + // it got GC'd + map.remove(value.key); + if (recordMiss) missSharedEvicted++; + return null; + } else { + return value.get(); + } + } + + public Object put(Object k, Object v) { + processQueue(); + return map.put(k, new SpecialValue(k,v)); + } + + public Set entrySet() { + return map.entrySet(); + } + + public void clear() { + processQueue(); + map.clear(); + } + + public int size() { + processQueue(); + return map.size(); + } + + public Object remove(Object k) { + processQueue(); + SpecialValue value = (SpecialValue)map.remove(k); + if (value==null) return null; + if (value.get()!=null) { + return value.get(); + } + return null; + } + } /** * 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; + private void storeClassAsReference(URL url, JavaClass clazz ) { + if (useSharedCache) { + clazz.setRepository(null); // can't risk setting repository, we'll get in a pickle! + sharedCache.put(url, clazz); + } else { + clazz.setRepository(this); + localCache.put(url, new SoftReference(clazz)); + } } /** - * 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 ) { - storeClassAsReference(clazz); + storeClassAsReference(toURL(clazz.getClassName()),clazz); } /** * Remove class from repository */ public void removeClass(JavaClass clazz) { - loadedClassesLocalCache.remove(clazz.getClassName()); + if (useSharedCache) sharedCache.remove(toURL(clazz.getClassName())); + else localCache.remove(toURL(clazz.getClassName())); } /** * Find an already defined JavaClass in the local cache. */ public JavaClass findClass( String className ) { - Object o = loadedClassesLocalCache.get( className ); + if (useSharedCache) return findClassShared(toURL(className)); + else return findClassLocal(toURL(className)); + } + + private JavaClass findClassLocal( URL url ) { + Object o = localCache.get(url); if (o != null) { o = ((Reference)o).get(); if (o != null) { @@ -162,55 +239,56 @@ public class ClassLoaderRepository implements Repository { * 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; + return (JavaClass)sharedCache.get(url); } + private URL toURL(String className) { + URL url = (URL)nameMap.get(className); + if (url==null) { + String classFile = className.replace('.', '/'); + url = loader.getResource( classFile + ".class" ); + nameMap.put(className, url); + } + return url; + } /** * Lookup a JavaClass object from the Class Name provided. */ public JavaClass loadClass( String className ) throws ClassNotFoundException { - String classFile = className.replace('.', '/'); - - // Check the local cache - JavaClass clazz = findClass(className); - if (clazz != null) { cacheHitsLocal++; return clazz; } + + // translate to a URL + long time = System.currentTimeMillis(); + java.net.URL url = toURL(className); + timeManipulatingURLs += (System.currentTimeMillis() - time); + if (url==null) throw new ClassNotFoundException(className + " not found."); + + JavaClass clazz = null; - try { - // 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 + // Look in the appropriate cache + if (useSharedCache) { clazz = findClassShared(url); - if (clazz != null) { cacheHitsShared++; timeSpentLoading+=(System.currentTimeMillis() - time); return clazz; } + if (clazz != null) { cacheHitsShared++; return clazz; } + } else { + clazz = findClassLocal(url); + if (clazz != null) { cacheHitsLocal++; return clazz; } + } + + // Didn't find it in either cache + misses++; - // Didn't find it in either cache - misses++; - - if (is == null) { // TODO move this up? - throw new ClassNotFoundException(className + " not found."); - } - - ClassParser parser = new ClassParser( is, className ); + try { + // Load it + String classFile = className.replace('.', '/'); + InputStream is = (useSharedCache?url.openStream():loader.getResourceAsStream( classFile + ".class" )); + if (is == null) { + throw new ClassNotFoundException(className + " not found."); + } + ClassParser parser = new ClassParser( is, className ); clazz = parser.parse(); - // Store it in both caches - Reference ref = storeClassAsReference( clazz ); - storeReferenceShared(url,ref); + // Cache it + storeClassAsReference(url, clazz ); timeSpentLoading += (System.currentTimeMillis() - time); classesLoadedCount++; @@ -221,27 +299,42 @@ public class ClassLoaderRepository implements Repository { } -/** + /** * Produce a report on cache usage. */ - public String reportAllStatistics() { + public String report() { StringBuffer sb = new StringBuffer(); sb.append("BCEL repository report."); - if (!useSharedCache) sb.append(" (Shared cache deactivated)"); + if (useSharedCache) sb.append(" (shared cache)"); + else sb.append(" (local cache)"); sb.append(" Total time spent loading: "+timeSpentLoading+"ms."); - sb.append(" Time manipulating URLs: "+timeManipulatingURLs+"ms."); + sb.append(" Time spent 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+")."); + if (useSharedCache) { + sb.append(" Shared cache size: "+sharedCache.size()); + sb.append(" Shared cache (hits/missDueToEviction): ("+cacheHitsShared+"/"+missSharedEvicted+")."); + } else { + sb.append(" Local cache size: "+localCache.size()); + sb.append(" Local cache (hits/missDueToEviction): ("+cacheHitsLocal+"/"+missLocalEvicted+")."); + } return sb.toString(); } - public int reportLocalCacheHits() { - return cacheHitsLocal; - } - - public static int reportSharedCacheHits() { - return cacheHitsShared; + /** + * Returns an array of the stats, for testing, the order is fixed: + * 0=time spent loading (static) + * 1=time spent manipulating URLs (static) + * 2=classes loaded (static) + * 3=cache hits shared (static) + * 4=misses in shared due to eviction (static) + * 5=cache hits local + * 6=misses in local due to eviction + * 7=shared cache size + */ + public long[] reportStats() { + return new long[]{timeSpentLoading,timeManipulatingURLs,classesLoadedCount, + cacheHitsShared,missSharedEvicted,cacheHitsLocal,missLocalEvicted, + sharedCache.size()}; } /** @@ -257,7 +350,6 @@ public class ClassLoaderRepository implements Repository { missLocalEvicted = 0; misses = 0; clear(); - clearShared(); } @@ -267,12 +359,10 @@ public class ClassLoaderRepository implements Repository { /** Clear all entries from the local cache */ public void clear() { - loadedClassesLocalCache.clear(); - } - - /** Clear all entries from the shared cache */ - public static void clearShared() { - loadedUrlsSharedCache.clear(); + if (useSharedCache) sharedCache.clear(); + else localCache.clear(); } + } + 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 index d9e0d6d58..7202be3fa 100644 --- a/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/ClassloaderRepositoryTest.java +++ b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/ClassloaderRepositoryTest.java @@ -26,20 +26,33 @@ public class ClassloaderRepositoryTest extends TestCase { // Retrieve string 5 times from same repository, 4 hits should be from local cache public void testLocalCacheWorks() throws ClassNotFoundException { + ClassLoaderRepository.useSharedCache=false; 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); + assertTrue("Should have used local cache 4 times: "+reportLocalCacheHits(rep1),reportLocalCacheHits(rep1)==4); + } + + // Retrieve string 5 times from same repository, 4 hits should be from local cache + public void testSharedCacheWorksOnOne() throws ClassNotFoundException { + ClassLoaderRepository.useSharedCache=true; + 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: "+reportSharedCacheHits(rep1),reportSharedCacheHits(rep1)==4); } // Retrieve String through one repository then load again through another, should be shared cache hit public void testSharedCacheWorks() throws ClassNotFoundException { + ClassLoaderRepository.useSharedCache=true; 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); + assertTrue("Should have retrieved String from shared cache: "+reportSharedCacheHits(rep1), + reportSharedCacheHits(rep1)==1); } // Shared cache OFF, shouldn't get a shared cache hit @@ -49,8 +62,8 @@ public class ClassloaderRepositoryTest extends TestCase { 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); + reportSharedCacheHits(rep1), + reportSharedCacheHits(rep1)==0); } finally { ClassLoaderRepository.useSharedCache=true; } @@ -58,10 +71,19 @@ public class ClassloaderRepositoryTest extends TestCase { public void tearDown() throws Exception { super.tearDown(); - System.err.println("Rep1: "+rep1.reportAllStatistics()); - System.err.println("Rep2: "+rep2.reportAllStatistics()); + System.err.println("Rep1: "+rep1.reportStats()); + System.err.println("Rep2: "+rep2.reportStats()); rep1.reset(); rep2.reset(); } - + + private long reportLocalCacheHits(ClassLoaderRepository rep) { + return rep.reportStats()[5]; + } + + private long reportSharedCacheHits(ClassLoaderRepository rep) { + return rep.reportStats()[3]; + } + } + |