src/share/classes/java/lang/reflect/Proxy.java

Print this page

        

@@ -31,14 +31,17 @@
 import java.security.PrivilegedAction;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.List;
 import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BiFunction;
 import sun.misc.ProxyGenerator;
 import sun.misc.VM;
 import sun.reflect.CallerSensitive;
 import sun.reflect.Reflection;
 import sun.reflect.misc.ReflectUtil;

@@ -230,31 +233,19 @@
  */
 public class Proxy implements java.io.Serializable {
 
     private static final long serialVersionUID = -2222568056686623797L;
 
-    /** prefix for all proxy class names */
-    private final static String proxyClassNamePrefix = "$Proxy";
-
     /** parameter types of a proxy class constructor */
-    private final static Class[] constructorParams =
+    private static final Class<?>[] constructorParams =
         { InvocationHandler.class };
 
-    /** maps a class loader to the proxy class cache for that loader */
-    private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache
-        = new WeakHashMap<>();
-
-    /** marks that a particular proxy class is currently being generated */
-    private static Object pendingGenerationMarker = new Object();
-
-    /** next number to use for generation of unique proxy class names */
-    private static long nextUniqueNumber = 0;
-    private static Object nextUniqueNumberLock = new Object();
-
-    /** set of all generated proxy classes, for isProxyClass implementation */
-    private static Map<Class<?>, Void> proxyClasses =
-        Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());
+    /**
+     * a cache of proxy classes
+     */
+    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
+        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
 
     /**
      * the invocation handler for this proxy instance.
      * @serial
      */

@@ -421,148 +412,127 @@
                                            Class<?>... interfaces) {
         if (interfaces.length > 65535) {
             throw new IllegalArgumentException("interface limit exceeded");
         }
 
-        Class<?> proxyClass = null;
-
-        /* collect interface names to use as key for proxy class cache */
-        String[] interfaceNames = new String[interfaces.length];
-
-        // for detecting duplicates
-        Set<Class<?>> interfaceSet = new HashSet<>();
-
-        for (int i = 0; i < interfaces.length; i++) {
+        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
+        for (Class<?> intf : interfaces) {
             /*
              * Verify that the class loader resolves the name of this
              * interface to the same Class object.
              */
-            String interfaceName = interfaces[i].getName();
             Class<?> interfaceClass = null;
             try {
-                interfaceClass = Class.forName(interfaceName, false, loader);
+                interfaceClass = Class.forName(intf.getName(), false, loader);
             } catch (ClassNotFoundException e) {
             }
-            if (interfaceClass != interfaces[i]) {
+            if (interfaceClass != intf) {
                 throw new IllegalArgumentException(
-                    interfaces[i] + " is not visible from class loader");
+                    intf + " is not visible from class loader");
             }
-
             /*
              * Verify that the Class object actually represents an
              * interface.
              */
             if (!interfaceClass.isInterface()) {
                 throw new IllegalArgumentException(
                     interfaceClass.getName() + " is not an interface");
             }
-
             /*
              * Verify that this interface is not a duplicate.
              */
-            if (interfaceSet.contains(interfaceClass)) {
+            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                 throw new IllegalArgumentException(
                     "repeated interface: " + interfaceClass.getName());
             }
-            interfaceSet.add(interfaceClass);
+        }
+
+        return proxyClassCache.get(loader, interfaces);
+    }
 
-            interfaceNames[i] = interfaceName;
+    /**
+     * A key composed of an array of interned strings
+     */
+    private static final class Key {
+        private final String[] strings;
+
+        Key(String[] strings) {
+            this.strings = strings;
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 0;
+            for (String s : strings) hash ^= System.identityHashCode(s);
+            return hash;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null || obj.getClass() != Key.class) return false;
+            String[] otherStrings = ((Key) obj).strings;
+            if (strings.length != otherStrings.length) return false;
+            for (int i = 0; i < strings.length; i++)
+                if (strings[i] != otherStrings[i]) return false;
+            return true;
+        }
         }
 
+    /**
+     * A function that maps an array of interfaces to a Key composed of interface names.
+     */
+    private static final class KeyFactory
+        implements BiFunction<ClassLoader, Class<?>[], Key>
+    {
+        @Override
+        public Key apply(ClassLoader classLoader, Class<?>[] interfaces) {
+            // collect interface names to use as key for proxy class cache
+            String[] interfaceNames = new String[interfaces.length];
+            for (int i = 0; i < interfaces.length; i++) {
+                interfaceNames[i] = interfaces[i].getName();
+            }
         /*
          * Using string representations of the proxy interfaces as
          * keys in the proxy class cache (instead of their Class
          * objects) is sufficient because we require the proxy
          * interfaces to be resolvable by name through the supplied
          * class loader, and it has the advantage that using a string
          * representation of a class makes for an implicit weak
          * reference to the class.
          */
-        List<String> key = Arrays.asList(interfaceNames);
-
-        /*
-         * Find or create the proxy class cache for the class loader.
-         */
-        Map<List<String>, Object> cache;
-        synchronized (loaderToCache) {
-            cache = loaderToCache.get(loader);
-            if (cache == null) {
-                cache = new HashMap<>();
-                loaderToCache.put(loader, cache);
+            return new Key(interfaceNames);
             }
-            /*
-             * This mapping will remain valid for the duration of this
-             * method, without further synchronization, because the mapping
-             * will only be removed if the class loader becomes unreachable.
-             */
         }
 
-        /*
-         * Look up the list of interfaces in the proxy class cache using
-         * the key.  This lookup will result in one of three possible
-         * kinds of values:
-         *     null, if there is currently no proxy class for the list of
-         *         interfaces in the class loader,
-         *     the pendingGenerationMarker object, if a proxy class for the
-         *         list of interfaces is currently being generated,
-         *     or a weak reference to a Class object, if a proxy class for
-         *         the list of interfaces has already been generated.
-         */
-        synchronized (cache) {
-            /*
-             * Note that we need not worry about reaping the cache for
-             * entries with cleared weak references because if a proxy class
-             * has been garbage collected, its class loader will have been
-             * garbage collected as well, so the entire cache will be reaped
-             * from the loaderToCache map.
-             */
-            do {
-                Object value = cache.get(key);
-                if (value instanceof Reference) {
-                    proxyClass = (Class<?>) ((Reference) value).get();
-                }
-                if (proxyClass != null) {
-                    // proxy class already generated: return it
-                    return proxyClass;
-                } else if (value == pendingGenerationMarker) {
-                    // proxy class being generated: wait for it
-                    try {
-                        cache.wait();
-                    } catch (InterruptedException e) {
-                        /*
-                         * The class generation that we are waiting for should
-                         * take a small, bounded time, so we can safely ignore
-                         * thread interrupts here.
-                         */
-                    }
-                    continue;
-                } else {
-                    /*
-                     * No proxy class for this list of interfaces has been
-                     * generated or is being generated, so we will go and
-                     * generate it now.  Mark it as pending generation.
+    /**
+     * A factory function that generates, defines and returns the proxy class given
+     * the ClassLoader and array of interfaces.
                      */
-                    cache.put(key, pendingGenerationMarker);
-                    break;
-                }
-            } while (true);
-        }
+    private static final class ProxyClassFactory
+        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
+    {
+        // prefix for all proxy class names
+        private static final String proxyClassNamePrefix = "$Proxy";
 
-        try {
+        // next number to use for generation of unique proxy class names
+        private static final AtomicLong nextUniqueNumber = new AtomicLong();
+
+        @Override
+        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
             String proxyPkg = null;     // package to define proxy class in
             int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
 
             /*
              * Record the package of a non-public proxy interface so that the
              * proxy class will be defined in the same package.  Verify that
              * all non-public proxy interfaces are in the same package.
              */
-            for (int i = 0; i < interfaces.length; i++) {
-                int flags = interfaces[i].getModifiers();
+            for (Class<?> intf : interfaces) {
+                int flags = intf.getModifiers();
                 if (!Modifier.isPublic(flags)) {
                     accessFlags = Modifier.FINAL;
-                    String name = interfaces[i].getName();
+                    String name = intf.getName();
                     int n = name.lastIndexOf('.');
                     String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                     if (proxyPkg == null) {
                         proxyPkg = pkg;
                     } else if (!pkg.equals(proxyPkg)) {

@@ -575,31 +545,23 @@
             if (proxyPkg == null) {
                 // if no non-public proxy interfaces, use com.sun.proxy package
                 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
             }
 
-            {
                 /*
                  * Choose a name for the proxy class to generate.
                  */
-                long num;
-                synchronized (nextUniqueNumberLock) {
-                    num = nextUniqueNumber++;
-                }
+            long num = nextUniqueNumber.getAndIncrement();
                 String proxyName = proxyPkg + proxyClassNamePrefix + num;
-                /*
-                 * Verify that the class loader hasn't already
-                 * defined a class with the chosen name.
-                 */
 
                 /*
                  * Generate the specified proxy class.
                  */
                 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                     proxyName, interfaces, accessFlags);
                 try {
-                    proxyClass = defineClass0(loader, proxyName,
+                return defineClass0(loader, proxyName,
                         proxyClassFile, 0, proxyClassFile.length);
                 } catch (ClassFormatError e) {
                     /*
                      * A ClassFormatError here means that (barring bugs in the
                      * proxy class generation code) there was some other

@@ -608,31 +570,10 @@
                      * exceeded).
                      */
                     throw new IllegalArgumentException(e.toString());
                 }
             }
-            // add to set of all generated proxy classes, for isProxyClass
-            proxyClasses.put(proxyClass, null);
-
-        } finally {
-            /*
-             * We must clean up the "pending generation" state of the proxy
-             * class cache entry somehow.  If a proxy class was successfully
-             * generated, store it in the cache (with a weak reference);
-             * otherwise, remove the reserved entry.  In all cases, notify
-             * all waiters on reserved entries in this cache.
-             */
-            synchronized (cache) {
-                if (proxyClass != null) {
-                    cache.put(key, new WeakReference<Class<?>>(proxyClass));
-                } else {
-                    cache.remove(key);
-                }
-                cache.notifyAll();
-            }
-        }
-        return proxyClass;
     }
 
     /**
      * Returns an instance of a proxy class for the specified interfaces
      * that dispatches method invocations to the specified invocation

@@ -755,25 +696,10 @@
                 sm.checkPermission(new ReflectPermission("newProxyInPackage." + pkg));
             }
         }
     }
 
-    private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
-        try {
-            return cons.newInstance(new Object[] {h} );
-        } catch (IllegalAccessException | InstantiationException e) {
-            throw new InternalError(e.toString(), e);
-        } catch (InvocationTargetException e) {
-            Throwable t = e.getCause();
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            } else {
-                throw new InternalError(t.toString(), t);
-            }
-        }
-    }
-
     /**
      * Returns true if and only if the specified class was dynamically
      * generated to be a proxy class using the {@code getProxyClass}
      * method or the {@code newProxyInstance} method.
      *

@@ -785,15 +711,11 @@
      * @return  {@code true} if the class is a proxy class and
      *          {@code false} otherwise
      * @throws  NullPointerException if {@code cl} is {@code null}
      */
     public static boolean isProxyClass(Class<?> cl) {
-        if (cl == null) {
-            throw new NullPointerException();
-        }
-
-        return proxyClasses.containsKey(cl);
+        return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
     }
 
     /**
      * Returns the invocation handler for the specified proxy instance.
      *