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

src/share/classes/java/lang/ClassLoader.java

Print this page

        

*** 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 **** */ 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>()); static { ! synchronized (loaderTypes) { loaderTypes.add(ClassLoader.class); } } /** ! * Registers the given class loader type as parallel capabale. * Returns {@code true} is successfully registered; {@code false} if * loader's super class is not registered. */ ! static boolean register(Class<? extends ClassLoader> c) { synchronized (loaderTypes) { ! if (loaderTypes.contains(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); return true; } else { return false; } } } /** ! * Returns {@code true} if the given class loader type is ! * registered as parallel capable. */ ! static boolean isRegistered(Class<? extends ClassLoader> c) { ! synchronized (loaderTypes) { ! return loaderTypes.contains(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; // 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]; --- 204,274 ---- */ private static class ParallelLoaders { private ParallelLoaders() {} // the set of parallel capable loader types ! // - 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.put(ClassLoader.class, Boolean.TRUE); } } /** ! * 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, ! 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.put(c, fullyConcurrent); ! System.out.println("Registered: " + c.getName() + " as " + ! (fullyConcurrent ? "fully concurrent " : " parallel capable") + " classloader"); return true; } else { return false; } } } /** ! * Returns the value of the given loader in the parallel ! * loader map, else null. */ ! 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 **** return null; } private ClassLoader(Void unused, ClassLoader parent) { this.parent = parent; ! if (ParallelLoaders.isRegistered(this.getClass())) { 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 parallelLockMap = null; package2certs = new Hashtable<>(); domains = new HashSet<>(); assertionLock = this; } --- 304,327 ---- return null; } private ClassLoader(Void unused, ClassLoader parent) { this.parent = parent; ! 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 **** * #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. * * @param name * The <a href="#name">binary name</a> of the class * * @param resolve --- 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, 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 **** 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 --- 484,495 ----
*** 462,481 **** lock = parallelLockMap.putIfAbsent(className, newLock); if (lock == null) { lock = newLock; } } 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) { synchronized (this) { return loadClass(name); } } else { return loadClass(name); --- 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 (!isParallelCapable()) { synchronized (this) { return loadClass(name); } } else { return loadClass(name);
*** 926,936 **** Certificate[] certs = null; if (cs != null) { certs = cs.getCertificates(); } Certificate[] pcerts = null; ! if (parallelLockMap == null) { synchronized (this) { pcerts = package2certs.get(pname); if (pcerts == null) { package2certs.put(pname, (certs == null? nocerts:certs)); } --- 983,993 ---- Certificate[] certs = null; if (cs != null) { certs = cs.getCertificates(); } Certificate[] pcerts = null; ! if (!isParallelCapable()) { synchronized (this) { pcerts = package2certs.get(pname); if (pcerts == null) { package2certs.put(pname, (certs == null? nocerts:certs)); }
*** 1228,1245 **** * 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> * * @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)); } /** * Find a resource of the specified name from the search path used to load * classes. This method locates the resource through the system class --- 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), 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