summaryrefslogtreecommitdiffstats
path: root/bcel-builder
diff options
context:
space:
mode:
authoraclement <aclement>2006-08-21 15:23:58 +0000
committeraclement <aclement>2006-08-21 15:23:58 +0000
commit0185a0214f790c6611b48b986e01ef97a399b6ae (patch)
tree9f7c1fa2002c7d29c08ef81da63e314ffe63f360 /bcel-builder
parentbec92132ab1ee779bc910c62fd8cb854973d06cf (diff)
downloadaspectj-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.java258
-rw-r--r--bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/ClassloaderRepositoryTest.java38
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];
+ }
+
}
+