--- old/src/java.base/share/classes/java/lang/ClassLoader.java 2014-10-21 12:05:55.287208971 +0200 +++ new/src/java.base/share/classes/java/lang/ClassLoader.java 2014-10-21 12:05:55.198210614 +0200 @@ -29,12 +29,10 @@ import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.net.MalformedURLException; import java.net.URL; import java.security.AccessController; import java.security.AccessControlContext; import java.security.CodeSource; -import java.security.Policy; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; @@ -54,7 +52,6 @@ import sun.misc.CompoundEnumeration; import sun.misc.Resource; import sun.misc.URLClassPath; -import sun.misc.VM; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; import sun.reflect.misc.ReflectUtil; @@ -236,11 +233,20 @@ } } + // A lock object with a boolean flag indicating that loading of + // a class with particular name has been attempted and attempt + // was successful. + // Note: this is used to avoid findLoadedClass() calls when + // 1st request for a particular class name arrives. + private static class Lock { + boolean attempted; + } + // 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 parallelLockMap; + private final ConcurrentHashMap parallelLockMap; // Hashtable that maps packages to certs private final Map package2certs; @@ -356,7 +362,28 @@ * If the class was not found */ public Class loadClass(String name) throws ClassNotFoundException { - return loadClass(name, false); + Thread ct = Thread.currentThread(); + ClassLoader prevLoader = ct.initiatingClassLoader; + ct.initiatingClassLoader = this; + try { + return loadClass(name, false); + } catch (ClassNotFoundException e) { + if (e.getStackTraceDepth() > 0) { + throw e; + } else { + // This should not happen if class loaders follow standard + // delegation model where ClassNotFoundException(s) from parent + // loader are always swallowed and only exceptions from the + // initiating class loader are propagated, but for other + // non-standard class loaders it may happen that exceptions from + // non-initiating class-loader are propagated. + // In that case we replace such stack-less exceptions with + // normal exceptions at exit from class loading request. + throw new ClassNotFoundException(e.getMessage(), e.getException()); + } + } finally { + ct.initiatingClassLoader = prevLoader; + } } /** @@ -403,9 +430,25 @@ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - synchronized (getClassLoadingLock(name)) { + Object cll = getClassLoadingLock(name); + synchronized (cll) { // First, check if the class has already been loaded - Class c = findLoadedClass(name); + Class c; + // Are we parallel capable and getClassLoadingLock() has + // not been overridden to supply it's own type of locks? + Lock lock = (cll instanceof Lock) ? (Lock) cll : null; + if (lock != null) { // yes + c = lock.attempted + // only invoke findLoadedClass() if loading of class + // with such name has already been attempted + ? findLoadedClass(name) + // else we can be sure findLoadedClass() would + // return null and can skip invoking it + : null; + } else { + // we are not parallel capable and must always invoke + c = findLoadedClass(name); + } if (c == null) { long t0 = System.nanoTime(); try { @@ -430,6 +473,12 @@ sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } + + // if we reach here, attempt was successful - we mark that + // in our Lock so next time findLoadedClass() will be invoked + if (lock != null) { + lock.attempted = true; + } } if (resolve) { resolveClass(c); @@ -461,7 +510,7 @@ protected Object getClassLoadingLock(String className) { Object lock = this; if (parallelLockMap != null) { - Object newLock = new Object(); + Lock newLock = new Lock(); lock = parallelLockMap.putIfAbsent(className, newLock); if (lock == null) { lock = newLock; @@ -529,7 +578,13 @@ * @since 1.2 */ protected Class findClass(String name) throws ClassNotFoundException { - throw new ClassNotFoundException(name); + // we only need stack trace when we're the initiating class loader. + boolean initiating = (Thread.currentThread().initiatingClassLoader == this); + throw new ClassNotFoundException( + name, + null, + initiating + ); } /**