--- old/src/share/classes/java/lang/ClassLoader.java 2012-12-05 06:55:48.132052889 -0500 +++ new/src/share/classes/java/lang/ClassLoader.java 2012-12-05 06:55:47.228003356 -0500 @@ -108,6 +108,19 @@ * duration of the class loading process (see {@link #loadClass * loadClass} methods). * + *

Regular parallel capable class loaders may still need to use some form + * of synchronization to control concurrent load attempts of a given class. + * The {@link #getClassLoadingLock getClassLoadingLock} method is used to + * provide that object for use by {@link #loadClass loadClass}. A class loader + * can support fully concurrent loading of any class, in which case no + * synchronization need be used. Such parallel capable class loaders are + * required to register themselves at their class initialization time by + * invoking the + * {@link #registerAsFullyConcurrent registerAsFullyConcurrent} method. + * Note that the ClassLoader class is registered as fully + * concurrent by default. However, its subclasses still need to register + * themselves if they capable of fully concurrent class loading. + * *

Normally, the Java virtual machine loads classes from the local file * system in a platform-dependent manner. For example, on UNIX systems, the * virtual machine loads classes from the directory defined by the @@ -193,27 +206,33 @@ private ParallelLoaders() {} // the set of parallel capable loader types - private static final Set> loaderTypes = - Collections.newSetFromMap( - new WeakHashMap, Boolean>()); - static { - synchronized (loaderTypes) { loaderTypes.add(ClassLoader.class); } + // - loader in the map -> parallel capable + // - loader maps to TRUE -> fully concurrent + private static final WeakHashMap, + Boolean> loaderTypes = + new WeakHashMap<>(); + + static { + synchronized (loaderTypes) { loaderTypes.put(ClassLoader.class, Boolean.TRUE); } } /** - * Registers the given class loader type as parallel capabale. + * Registers the given class loader type as parallel capable. * Returns {@code true} is successfully registered; {@code false} if * loader's super class is not registered. */ - static boolean register(Class c) { + static boolean register(Class c, + boolean fullyConcurrent) { synchronized (loaderTypes) { - if (loaderTypes.contains(c.getSuperclass())) { + if (loaderTypes.containsKey(c.getSuperclass())) { // register the class loader as parallel capable // if and only if all of its super classes are. // Note: given current classloading sequence, if // the immediate super class is parallel capable, // all the super classes higher up must be too. - loaderTypes.add(c); + loaderTypes.put(c, fullyConcurrent); + System.out.println("Registered: " + c.getName() + " as " + + (fullyConcurrent ? "fully concurrent " : " parallel capable") + " classloader"); return true; } else { return false; @@ -222,12 +241,12 @@ } /** - * Returns {@code true} if the given class loader type is - * registered as parallel capable. + * Returns the value of the given loader in the parallel + * loader map, else null. */ - static boolean isRegistered(Class c) { - synchronized (loaderTypes) { - return loaderTypes.contains(c); + static Boolean getRegistration(Class c) { + synchronized(loaderTypes) { + return loaderTypes.get(c); } } } @@ -238,6 +257,16 @@ // is parallel capable and the appropriate lock object for class loading. private final ConcurrentHashMap parallelLockMap; + // Indicates this parallel capable loader supports fully concurrent loading + // Note: the VM uses this field to decide if the current class loader + // allows parallel class definition + private final boolean isFullyConcurrent; + + // Indicate if this loader is parallel capable + private boolean isParallelCapable() { + return parallelLockMap != null || isFullyConcurrent; + } + // Hashtable that maps packages to certs private final Map package2certs; @@ -277,14 +306,20 @@ private ClassLoader(Void unused, ClassLoader parent) { this.parent = parent; - if (ParallelLoaders.isRegistered(this.getClass())) { - parallelLockMap = new ConcurrentHashMap<>(); + Boolean reg = ParallelLoaders.getRegistration(this.getClass()); + if (reg != null) { + isFullyConcurrent = reg; + if (isFullyConcurrent) + parallelLockMap = null; + else + parallelLockMap = new ConcurrentHashMap<>(); package2certs = new ConcurrentHashMap<>(); domains = Collections.synchronizedSet(new HashSet()); assertionLock = new Object(); } else { // no finer-grained lock; lock on the classloader instance + isFullyConcurrent = false; parallelLockMap = null; package2certs = new Hashtable<>(); domains = new HashSet<>(); @@ -382,9 +417,11 @@ *

Subclasses of ClassLoader are encouraged to override {@link * #findClass(String)}, rather than this method.

* - *

Unless overridden, this method synchronizes on the result of - * {@link #getClassLoadingLock getClassLoadingLock} method - * during the entire class loading process. + *

Unless overridden, if this loader is not fully concurrent then + * this method synchronizes on the result + * of the {@link #getClassLoadingLock getClassLoadingLock} method + * during the entire class loading process. For a fully concurrent loader + * no synchronization occurs. * * @param name * The binary name of the class @@ -400,39 +437,56 @@ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - synchronized (getClassLoadingLock(name)) { - // First, check if the class has already been loaded - Class c = findLoadedClass(name); - if (c == null) { - long t0 = System.nanoTime(); - try { - if (parent != null) { - c = parent.loadClass(name, false); - } else { - c = findBootstrapClassOrNull(name); - } - } catch (ClassNotFoundException e) { - // ClassNotFoundException thrown if class not found - // from the non-null parent class loader - } - - if (c == null) { - // If still not found, then invoke findClass in order - // to find the class. - long t1 = System.nanoTime(); - c = findClass(name); - - // this is the defining class loader; record the stats - sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); - sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); - sun.misc.PerfCounter.getFindClasses().increment(); + Class c = null; + if (!isFullyConcurrent) { + synchronized (getClassLoadingLock(name)) { + c = do_loadClass(name); + if (resolve) { + resolveClass(c); } } + } + else { + c = do_loadClass(name); if (resolve) { resolveClass(c); } - return c; } + return c; + } + + + private Class do_loadClass(String name) + throws ClassNotFoundException + { + // First, check if the class has already been loaded + Class c = findLoadedClass(name); + if (c == null) { + long t0 = System.nanoTime(); + try { + if (parent != null) { + c = parent.loadClass(name, false); + } else { + c = findBootstrapClassOrNull(name); + } + } catch (ClassNotFoundException e) { + // ClassNotFoundException thrown if class not found + // from the non-null parent class loader + } + + if (c == null) { + // If still not found, then invoke findClass in order + // to find the class. + long t1 = System.nanoTime(); + c = findClass(name); + + // this is the defining class loader; record the stats + sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); + sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); + sun.misc.PerfCounter.getFindClasses().increment(); + } + } + return c; } /** @@ -464,6 +518,9 @@ lock = newLock; } } + else if (isFullyConcurrent) { + lock = null; // TBD: is this the best thing to return? + } return lock; } @@ -473,7 +530,7 @@ { // For backward compatibility, explicitly lock on 'this' when // the current class loader is not parallel capable. - if (parallelLockMap == null) { + if (!isParallelCapable()) { synchronized (this) { return loadClass(name); } @@ -928,7 +985,7 @@ certs = cs.getCertificates(); } Certificate[] pcerts = null; - if (parallelLockMap == null) { + if (!isParallelCapable()) { synchronized (this) { pcerts = package2certs.get(pname); if (pcerts == null) { @@ -1230,6 +1287,9 @@ * registered as parallel capable

* Note that once a class loader is registered as parallel capable, there * is no way to change it back.

+ *

+ * Class loaders that are fully concurrent parallel capable should + * use {@link #registerAsFullyConcurrent} to register.

* * @return true if the caller is successfully registered as * parallel capable and false if otherwise. @@ -1237,7 +1297,27 @@ * @since 1.7 */ protected static boolean registerAsParallelCapable() { - return ParallelLoaders.register(getCaller(1)); + return ParallelLoaders.register(getCaller(1), false); + } + + /** + * Registers the caller as a fully concurrent parallel capable + * class loader.

+ * The registration succeeds if and only if all of the following + * conditions are met:
+ * 1. no instance of the caller has been created

+ * 2. all of the super classes (except class Object) of the caller are + * registered as parallel capable

+ * Note that once a class loader is registered as fully concurrent + * parallel capable, there is no way to change it back.

+ * + * @return true if the caller is successfully registered as + * fully concurrent parallel capable and false if otherwise. + * + * @since 1.8 + */ + protected static boolean registerAsFullyConcurrent() { + return ParallelLoaders.register(getCaller(1), true); } /** --- old/src/share/classes/java/net/URLClassLoader.java 2012-12-05 06:55:50.948207189 -0500 +++ new/src/share/classes/java/net/URLClassLoader.java 2012-12-05 06:55:50.032156998 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -748,14 +748,14 @@ } } ); - ClassLoader.registerAsParallelCapable(); + ClassLoader.registerAsFullyConcurrent(); } } final class FactoryURLClassLoader extends URLClassLoader { static { - ClassLoader.registerAsParallelCapable(); + ClassLoader.registerAsFullyConcurrent(); } FactoryURLClassLoader(URL[] urls, ClassLoader parent, --- old/src/share/classes/java/security/SecureClassLoader.java 2012-12-05 06:55:53.800363463 -0500 +++ new/src/share/classes/java/security/SecureClassLoader.java 2012-12-05 06:55:52.892313709 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,7 +55,7 @@ private static final Debug debug = Debug.getInstance("scl"); static { - ClassLoader.registerAsParallelCapable(); + ClassLoader.registerAsFullyConcurrent(); } /** --- old/src/share/classes/sun/misc/Launcher.java 2012-12-05 06:55:56.640519080 -0500 +++ new/src/share/classes/sun/misc/Launcher.java 2012-12-05 06:55:55.728469107 -0500 @@ -122,7 +122,7 @@ static class ExtClassLoader extends URLClassLoader { static { - ClassLoader.registerAsParallelCapable(); + ClassLoader.registerAsFullyConcurrent(); } /** @@ -259,7 +259,7 @@ static class AppClassLoader extends URLClassLoader { static { - ClassLoader.registerAsParallelCapable(); + ClassLoader.registerAsFullyConcurrent(); } public static ClassLoader getAppClassLoader(final ClassLoader extcl)