Hi, There are several places in our codebase were we need/want to cache stuff that is associated with a class or class loader. Currently these places retain a strong reference to the class loader, which is not correct, because the class loader should be garbage collectable.
I cooked up proposal for an API to use for this and implemented it in the three places I could think of off the top my head (serialization, proxy and resource bundle). This is just a first stab, so no documentation and no change log entry yet. Please comment on the idea and/or the API. Regards, Jeroen
? gnu/classpath/ClassLoaderCache.java
? vm/reference/gnu/classpath/VMClassLoaderCache.java
Index: java/io/ObjectStreamClass.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/io/ObjectStreamClass.java,v
retrieving revision 1.45
diff -u -r1.45 ObjectStreamClass.java
--- java/io/ObjectStreamClass.java 21 May 2006 20:53:14 -0000 1.45
+++ java/io/ObjectStreamClass.java 21 Aug 2006 09:20:19 -0000
@@ -39,6 +39,7 @@
package java.io;
+import gnu.classpath.ClassLoaderCache;
import gnu.java.io.NullOutputStream;
import gnu.java.lang.reflect.TypeSignature;
import gnu.java.security.action.SetAccessibleAction;
@@ -100,14 +101,15 @@
if (cl == null)
return null;
- ObjectStreamClass osc = (ObjectStreamClass) classLookupTable.get(cl);
+ ObjectStreamClass osc =
+ (ObjectStreamClass) classLookupTable.get(cl.getClassLoader(), cl);
if (osc != null)
return osc;
else
{
osc = new ObjectStreamClass(cl);
- classLookupTable.put(cl, osc);
+ classLookupTable.put(cl.getClassLoader(), cl, osc);
return osc;
}
}
@@ -948,7 +950,7 @@
public static final ObjectStreamField[] NO_FIELDS = {};
- private static Hashtable classLookupTable = new Hashtable();
+ private static ClassLoaderCache classLookupTable = new ClassLoaderCache();
private static final NullOutputStream nullOutputStream = new
NullOutputStream();
private static final Comparator interfaceComparator = new
InterfaceComparator();
private static final Comparator memberComparator = new MemberComparator();
Index: java/lang/ClassLoader.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/lang/ClassLoader.java,v
retrieving revision 1.61
diff -u -r1.61 ClassLoader.java
--- java/lang/ClassLoader.java 23 Apr 2006 09:52:37 -0000 1.61
+++ java/lang/ClassLoader.java 21 Aug 2006 09:19:13 -0000
@@ -57,6 +57,7 @@
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
+import java.util.IdentityHashMap;
import java.util.Map;
import java.util.StringTokenizer;
@@ -145,6 +146,11 @@
*/
private final boolean initialized;
+ /**
+ * Cache field for use by gnu.classpath.ClassLoaderCache.
+ */
+ IdentityHashMap cache;
+
static class StaticData
{
/**
Index: java/lang/reflect/Proxy.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/lang/reflect/Proxy.java,v
retrieving revision 1.26
diff -u -r1.26 Proxy.java
--- java/lang/reflect/Proxy.java 20 Feb 2006 20:21:26 -0000 1.26
+++ java/lang/reflect/Proxy.java 21 Aug 2006 09:21:40 -0000
@@ -38,6 +38,7 @@
package java.lang.reflect;
+import gnu.classpath.ClassLoaderCache;
import gnu.java.lang.reflect.TypeSignature;
import java.io.Serializable;
@@ -167,12 +168,8 @@
/**
* Map of ProxyType to proxy class.
- *
- * @XXX This prevents proxy classes from being garbage collected.
- * java.util.WeakHashSet is not appropriate, because that collects the
- * keys, but we are interested in collecting the elements.
*/
- private static final Map proxyClasses = new HashMap();
+ private static final ClassLoaderCache proxyClasses = new ClassLoaderCache();
/**
* The invocation handler for this proxy instance. For Proxy, this
@@ -260,7 +257,7 @@
{
interfaces = (Class[]) interfaces.clone();
ProxyType pt = new ProxyType(loader, interfaces);
- Class clazz = (Class) proxyClasses.get(pt);
+ Class clazz = (Class) proxyClasses.get(loader, pt);
if (clazz == null)
{
if (VMProxy.HAVE_NATIVE_GET_PROXY_CLASS)
@@ -276,7 +273,7 @@
: new ClassFactory(data).generate(loader));
}
- Object check = proxyClasses.put(pt, clazz);
+ Object check = proxyClasses.put(loader, pt, clazz);
// assert check == null && clazz != null;
if (check != null || clazz == null)
throw new InternalError(/*"Fatal flaw in getProxyClass"*/);
@@ -364,7 +361,7 @@
return false;
// This is a linear search, even though we could do an O(1) search
// using new ProxyType(clazz.getClassLoader(), clazz.getInterfaces()).
- return proxyClasses.containsValue(clazz);
+ return proxyClasses.containsValue(clazz.getClassLoader(), clazz);
}
/**
Index: java/util/ResourceBundle.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/util/ResourceBundle.java,v
retrieving revision 1.38
diff -u -r1.38 ResourceBundle.java
--- java/util/ResourceBundle.java 13 Aug 2006 01:06:19 -0000 1.38
+++ java/util/ResourceBundle.java 21 Aug 2006 09:23:23 -0000
@@ -39,6 +39,7 @@
package java.util;
+import gnu.classpath.ClassLoaderCache;
import gnu.classpath.VMStackWalker;
import java.io.IOException;
@@ -121,11 +122,13 @@
*
* @see BundleKey
*/
- private static Map bundleCache = new LinkedHashMap(CACHE_SIZE + 1, 0.75F,
true)
- {
- public boolean removeEldestEntry(Map.Entry entry)
- {
- return size() > CACHE_SIZE;
+ private static ClassLoaderCache bundleCache = new ClassLoaderCache() {
+ protected Map newMap() {
+ return new LinkedHashMap(CACHE_SIZE + 1, 0.75F, true) {
+ public boolean removeEldestEntry(Map.Entry entry) {
+ return size() > CACHE_SIZE;
+ }
+ };
}
};
@@ -385,7 +388,7 @@
Locale defaultLocale = Locale.getDefault();
// This will throw NullPointerException if any arguments are null.
lookupKey.set(defaultLocale, baseName, locale, classLoader);
- Object obj = bundleCache.get(lookupKey);
+ Object obj = bundleCache.get(classLoader, lookupKey);
if (obj instanceof ResourceBundle)
return (ResourceBundle) obj;
@@ -406,14 +409,14 @@
if (bundle == null)
{
// Cache the fact that this lookup has previously failed.
- bundleCache.put(key, nullEntry);
+ bundleCache.put(classLoader, key, nullEntry);
throw new MissingResourceException("Bundle " + baseName
+ " not found for locale " + locale
+ " by classloader " + classLoader,
baseName, "");
}
// Cache the result and return it.
- bundleCache.put(key, bundle);
+ bundleCache.put(classLoader, key, bundle);
return bundle;
}
ClassLoaderCache.java
Description: ClassLoaderCache.java
VMClassLoaderCache.java
Description: VMClassLoaderCache.java
