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;
   }
 

Attachment: ClassLoaderCache.java
Description: ClassLoaderCache.java

Attachment: VMClassLoaderCache.java
Description: VMClassLoaderCache.java

Reply via email to