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