src/share/classes/java/lang/ClassLoader.java
Index Unified diffs Context diffs Sdiffs Wdiffs Patch New Old Previous File Next File
*** old/src/share/classes/java/lang/ClassLoader.java	Wed Dec  5 06:55:48 2012
--- new/src/share/classes/java/lang/ClassLoader.java	Wed Dec  5 06:55:47 2012

*** 106,115 **** --- 106,128 ---- * hierarchical, class loaders need to be parallel capable, otherwise class * loading can lead to deadlocks because the loader lock is held for the * duration of the class loading process (see {@link #loadClass * <tt>loadClass</tt>} methods). * + * <p>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 <tt>ClassLoader</tt> class is registered as fully + * concurrent by default. However, its subclasses still need to register + * themselves if they capable of fully concurrent class loading. + * * <p> 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 * <tt>CLASSPATH</tt> environment variable. *
*** 191,245 **** --- 204,274 ---- */ private static class ParallelLoaders { private ParallelLoaders() {} // the set of parallel capable loader types private static final Set<Class<? extends ClassLoader>> loaderTypes = Collections.newSetFromMap( new WeakHashMap<Class<? extends ClassLoader>, Boolean>()); + // - loader in the map -> parallel capable + // - loader maps to TRUE -> fully concurrent + private static final WeakHashMap<Class<? extends ClassLoader>, + Boolean> loaderTypes = + new WeakHashMap<>(); + static { ! synchronized (loaderTypes) { loaderTypes.add(ClassLoader.class); } ! 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<? extends ClassLoader> c) { + static boolean register(Class<? extends ClassLoader> c, + boolean fullyConcurrent) { synchronized (loaderTypes) { ! 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; } } } /** ! * 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<? extends ClassLoader> c) { - synchronized (loaderTypes) { ! return loaderTypes.contains(c); ! static Boolean getRegistration(Class<? extends ClassLoader> c) { ! synchronized(loaderTypes) { ! return loaderTypes.get(c); } } } // Maps class name to the corresponding lock object when the current // class loader is parallel capable. // Note: VM also uses this field to decide if the current class loader // is parallel capable and the appropriate lock object for class loading. private final ConcurrentHashMap<String, Object> 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 <String, Certificate[]> package2certs; // Shared among all packages with unsigned classes private static final Certificate[] nocerts = new Certificate[0];
*** 275,292 **** --- 304,327 ---- return null; } private ClassLoader(Void unused, ClassLoader parent) { this.parent = parent; if (ParallelLoaders.isRegistered(this.getClass())) { + 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<ProtectionDomain>()); assertionLock = new Object(); } else { // no finer-grained lock; lock on the classloader instance + isFullyConcurrent = false; parallelLockMap = null; package2certs = new Hashtable<>(); domains = new HashSet<>(); assertionLock = this; }
*** 380,392 **** --- 415,429 ---- * #resolveClass(Class)} method on the resulting <tt>Class</tt> object. * * <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link * #findClass(String)}, rather than this method. </p> * ! * <p> Unless overridden, this method synchronizes on the result of * {@link #getClassLoadingLock <tt>getClassLoadingLock</tt>} method * during the entire class loading process. ! * <p> Unless overridden, if this loader is not fully concurrent then + * this method synchronizes on the result + * of the {@link #getClassLoadingLock <tt>getClassLoadingLock</tt>} method + * during the entire class loading process. For a fully concurrent loader + * no synchronization occurs. * * @param name * The <a href="#name">binary name</a> of the class * * @param resolve
*** 398,408 **** --- 435,466 ---- * If the class could not be found */ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { + 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; + } + + + 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 {
*** 426,441 **** --- 484,495 ---- sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } } /** * Returns the lock object for class loading operations. * For backward compatibility, the default implementation of this method * behaves as follows. If this ClassLoader object is registered as
*** 462,481 **** --- 516,538 ---- lock = parallelLockMap.putIfAbsent(className, newLock); if (lock == null) { lock = newLock; } } + else if (isFullyConcurrent) { + lock = null; // TBD: is this the best thing to return? + } return lock; } // This method is invoked by the virtual machine to load a class. private Class<?> loadClassInternal(String name) throws ClassNotFoundException { // 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); } } else { return loadClass(name);
*** 926,936 **** --- 983,993 ---- Certificate[] certs = null; if (cs != null) { certs = cs.getCertificates(); } Certificate[] pcerts = null; ! if (parallelLockMap == null) { ! if (!isParallelCapable()) { synchronized (this) { pcerts = package2certs.get(pname); if (pcerts == null) { package2certs.put(pname, (certs == null? nocerts:certs)); }
*** 1228,1245 **** --- 1285,1325 ---- * 1. no instance of the caller has been created</p> * 2. all of the super classes (except class Object) of the caller are * registered as parallel capable</p> * Note that once a class loader is registered as parallel capable, there * is no way to change it back. </p> + * <p> + * Class loaders that are fully concurrent parallel capable should + * use {@link #registerAsFullyConcurrent} to register. </p> * * @return true if the caller is successfully registered as * parallel capable and false if otherwise. * * @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.</p> + * The registration succeeds if and only if all of the following + * conditions are met: <br> + * 1. no instance of the caller has been created</p> + * 2. all of the super classes (except class Object) of the caller are + * registered as parallel capable</p> + * Note that once a class loader is registered as fully concurrent + * parallel capable, there is no way to change it back. </p> + * + * @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); } /** * Find a resource of the specified name from the search path used to load * classes. This method locates the resource through the system class

src/share/classes/java/lang/ClassLoader.java
Index Unified diffs Context diffs Sdiffs Wdiffs Patch New Old Previous File Next File