--- old/src/java.base/share/classes/java/lang/ClassLoader.java 2017-10-09 12:52:46.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/ClassLoader.java 2017-10-09 12:52:46.000000000 -0700 @@ -37,17 +37,20 @@ import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.security.cert.Certificate; +import java.util.Arrays; import java.util.Collections; +import java.util.Deque; import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.Hashtable; +import java.util.LinkedList; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.Spliterator; import java.util.Spliterators; -import java.util.Stack; import java.util.Vector; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; @@ -58,9 +61,9 @@ import jdk.internal.perf.PerfCounter; import jdk.internal.loader.BootLoader; import jdk.internal.loader.ClassLoaders; -import jdk.internal.misc.SharedSecrets; import jdk.internal.misc.Unsafe; import jdk.internal.misc.VM; +import jdk.internal.ref.CleanerFactory; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; import sun.reflect.misc.ReflectUtil; @@ -2375,72 +2378,160 @@ * @since 1.2 */ static class NativeLibrary { - // opaque handle to native library, used in native code. - long handle; - // the version of JNI environment the native library requires. - private int jniVersion; // the class from which the library is loaded, also indicates // the loader this native library belongs. - private final Class fromClass; + final Class fromClass; // the canonicalized name of the native library. // or static library name - String name; + final String name; // Indicates if the native library is linked into the VM - boolean isBuiltin; - // Indicates if the native library is loaded - boolean loaded; - native void load(String name, boolean isBuiltin); + final boolean isBuiltin; + + // opaque handle to native library, used in native code. + long handle; + // the version of JNI environment the native library requires. + int jniVersion; - native long find(String name); - native void unload(String name, boolean isBuiltin); + native boolean load0(String name, boolean isBuiltin); - public NativeLibrary(Class fromClass, String name, boolean isBuiltin) { + native long findEntry(String name); + + NativeLibrary(Class fromClass, String name, boolean isBuiltin) { this.name = name; this.fromClass = fromClass; this.isBuiltin = isBuiltin; } - @SuppressWarnings("deprecation") - protected void finalize() { + /* + * Loads the native library and registers for cleanup when its + * associated class loader is unloaded + */ + boolean load() { + if (handle != 0) { + throw new InternalError("Native library " + name + " has been loaded"); + } + + if (!load0(name, isBuiltin)) return false; + + // register the class loader for cleanup when unloaded + // built class loaders are never unloaded + ClassLoader loader = fromClass.getClassLoader(); + if (loader != null && + loader != getBuiltinPlatformClassLoader() && + loader != getBuiltinAppClassLoader()) { + CleanerFactory.cleaner().register(loader, + new Unloader(name, handle, isBuiltin)); + } + return true; + } + + static boolean loadLibrary(Class fromClass, String name, boolean isBuiltin) { + ClassLoader loader = + fromClass == null ? null : fromClass.getClassLoader(); + synchronized (loadedLibraryNames) { - if (fromClass.getClassLoader() != null && loaded) { - /* remove the native library name */ - int size = loadedLibraryNames.size(); - for (int i = 0; i < size; i++) { - if (name.equals(loadedLibraryNames.elementAt(i))) { - loadedLibraryNames.removeElementAt(i); - break; + Map libs = + loader != null ? loader.nativeLibraries() : systemNativeLibraries(); + if (libs.containsKey(name)) { + return true; + } + + if (loadedLibraryNames.contains(name)) { + throw new UnsatisfiedLinkError("Native Library " + name + + " already loaded in another classloader"); + } + + /* + * When a library is being loaded, JNI_OnLoad function can cause + * another loadLibrary invocation that should succeed. + * + * We use a static stack to hold the list of libraries we are + * loading because this can happen only when called by the + * same thread because Runtime.load and Runtime.loadLibrary + * are synchronous. + * + * If there is a pending load operation for the library, we + * immediately return success; otherwise, we raise + * UnsatisfiedLinkError. + */ + for (NativeLibrary lib : nativeLibraryContext) { + if (name.equals(lib.name)) { + if (loader == lib.fromClass.getClassLoader()) { + return true; + } else { + throw new UnsatisfiedLinkError("Native Library " + + name + " is being loaded in another classloader"); } } - /* unload the library. */ - ClassLoader.nativeLibraryContext.push(this); - try { - unload(name, isBuiltin); - } finally { - ClassLoader.nativeLibraryContext.pop(); - } } + NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin); + // load the native library + nativeLibraryContext.push(lib); + try { + if (!lib.load()) return false; + } finally { + nativeLibraryContext.pop(); + } + // register the loaded native library + loadedLibraryNames.add(name); + libs.put(name, lib); } + return true; } - // Invoked in the VM to determine the context class in - // JNI_Load/JNI_Unload + + // Invoked in the VM to determine the context class in JNI_OnLoad + // and JNI_OnUnload static Class getFromClass() { - return ClassLoader.nativeLibraryContext.peek().fromClass; + return nativeLibraryContext.peek().fromClass; } - } - // All native library names we've loaded. - private static Vector loadedLibraryNames = new Vector<>(); + // native libraries being loaded + static Deque nativeLibraryContext = new LinkedList<>(); - // Native libraries belonging to system classes. - private static Vector systemNativeLibraries - = new Vector<>(); + /* + * The run() method will be invoked when this class loader becomes + * phantom reachable to unload the native library. + */ + static class Unloader implements Runnable { + // This represents the context when a native library is unloaded + // and getFromClass() will return null, + static final NativeLibrary UNLOADER = + new NativeLibrary(null, "dummy", false); + final String name; + final long handle; + final boolean isBuiltin; + + Unloader(String name, long handle, boolean isBuiltin) { + if (handle == 0) { + throw new IllegalArgumentException( + "Invalid handle for native library " + name); + } - // Native libraries associated with the class loader. - private Vector nativeLibraries = new Vector<>(); + this.name = name; + this.handle = handle; + this.isBuiltin = isBuiltin; + } - // native libraries being loaded/unloaded. - private static Stack nativeLibraryContext = new Stack<>(); + @Override + public void run() { + synchronized (loadedLibraryNames) { + /* remove the native library name */ + loadedLibraryNames.remove(name); + nativeLibraryContext.push(UNLOADER); + try { + unload(name, isBuiltin, handle); + } finally { + nativeLibraryContext.pop(); + } + + } + } + } + + // JNI FindClass expects the caller class if invoked from JNI_OnLoad + // and JNI_OnUnload is NativeLibrary class + static native void unload(String name, boolean isBuiltin, long handle); + } // The paths searched for libraries private static String usr_paths[]; @@ -2453,7 +2544,7 @@ int psCount = 0; if (ClassLoaderHelper.allowsQuotedPathElements && - ldPath.indexOf('\"') >= 0) { + ldPath.indexOf('\"') >= 0) { // First, remove quotes put around quoted parts of paths. // Second, use a quotation mark as a new path separator. // This will preserve any quoted old path separators. @@ -2463,7 +2554,7 @@ char ch = ldPath.charAt(i); if (ch == '\"') { while (++i < ldLen && - (ch = ldPath.charAt(i)) != '\"') { + (ch = ldPath.charAt(i)) != '\"') { buf[bufLen++] = ch; } } else { @@ -2479,7 +2570,7 @@ ps = '\"'; } else { for (int i = ldPath.indexOf(ps); i >= 0; - i = ldPath.indexOf(ps, i + 1)) { + i = ldPath.indexOf(ps, i + 1)) { psCount++; } } @@ -2489,11 +2580,11 @@ for (int j = 0; j < psCount; ++j) { int pathEnd = ldPath.indexOf(ps, pathStart); paths[j] = (pathStart < pathEnd) ? - ldPath.substring(pathStart, pathEnd) : "."; + ldPath.substring(pathStart, pathEnd) : "."; pathStart = pathEnd + 1; } paths[psCount] = (pathStart < ldLen) ? - ldPath.substring(pathStart, ldLen) : "."; + ldPath.substring(pathStart, ldLen) : "."; return paths; } @@ -2518,7 +2609,7 @@ File libfile = new File(libfilename); if (!libfile.isAbsolute()) { throw new UnsatisfiedLinkError( - "ClassLoader.findLibrary failed to return an absolute path: " + libfilename); + "ClassLoader.findLibrary failed to return an absolute path: " + libfilename); } if (loadLibrary0(fromClass, libfile)) { return; @@ -2549,10 +2640,11 @@ } } // Oops, it failed - throw new UnsatisfiedLinkError("no " + name + " in java.library.path"); + throw new UnsatisfiedLinkError("no " + name + + " in java.library.path: " + Arrays.toString(usr_paths)); } - static native String findBuiltinLib(String name); + private static native String findBuiltinLib(String name); private static boolean loadLibrary0(Class fromClass, final File file) { // Check to see if we're attempting to access a static library @@ -2573,86 +2665,73 @@ return false; } } - ClassLoader loader = - (fromClass == null) ? null : fromClass.getClassLoader(); - Vector libs = - loader != null ? loader.nativeLibraries : systemNativeLibraries; - synchronized (libs) { - int size = libs.size(); - for (int i = 0; i < size; i++) { - NativeLibrary lib = libs.elementAt(i); - if (name.equals(lib.name)) { - return true; - } - } + return NativeLibrary.loadLibrary(fromClass, name, isBuiltin); + } + + /* + * Invoked in the VM class linking code. + */ + private static long findNative(ClassLoader loader, String name) { + Map libs = + loader != null ? loader.nativeLibraries() : systemNativeLibraries(); + if (libs.isEmpty()) + return 0; + + // the native libraries map may be updated in another thread + // when a native library is being loaded. No symbol will be + // searched from it yet. + for (NativeLibrary lib : libs.values()) { + long entry = lib.findEntry(name); + if (entry != 0) return entry; + } + return 0; + } + + // All native library names we've loaded. + // This also serves as the lock to obtain nativeLibraries + // and write to nativeLibraryContext. + private static final Set loadedLibraryNames = new HashSet<>(); + // Native libraries belonging to system classes. + private static volatile Map systemNativeLibraries; + + // Native libraries associated with the class loader. + private volatile Map nativeLibraries; + + /* + * Returns the native libraries map associated with bootstrap class loader + * This method will create the map at the first time when called. + */ + private static Map systemNativeLibraries() { + Map libs = systemNativeLibraries; + if (libs == null) { synchronized (loadedLibraryNames) { - if (loadedLibraryNames.contains(name)) { - throw new UnsatisfiedLinkError - ("Native Library " + - name + - " already loaded in another classloader"); - } - /* If the library is being loaded (must be by the same thread, - * because Runtime.load and Runtime.loadLibrary are - * synchronous). The reason is can occur is that the JNI_OnLoad - * function can cause another loadLibrary invocation. - * - * Thus we can use a static stack to hold the list of libraries - * we are loading. - * - * If there is a pending load operation for the library, we - * immediately return success; otherwise, we raise - * UnsatisfiedLinkError. - */ - int n = nativeLibraryContext.size(); - for (int i = 0; i < n; i++) { - NativeLibrary lib = nativeLibraryContext.elementAt(i); - if (name.equals(lib.name)) { - if (loader == lib.fromClass.getClassLoader()) { - return true; - } else { - throw new UnsatisfiedLinkError - ("Native Library " + - name + - " is being loaded in another classloader"); - } - } + libs = systemNativeLibraries; + if (libs == null) { + libs = systemNativeLibraries = new ConcurrentHashMap<>(); } - NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin); - nativeLibraryContext.push(lib); - try { - lib.load(name, isBuiltin); - } finally { - nativeLibraryContext.pop(); - } - if (lib.loaded) { - loadedLibraryNames.addElement(name); - libs.addElement(lib); - return true; - } - return false; } } + return libs; } - // Invoked in the VM class linking code. - static long findNative(ClassLoader loader, String name) { - Vector libs = - loader != null ? loader.nativeLibraries : systemNativeLibraries; - synchronized (libs) { - int size = libs.size(); - for (int i = 0; i < size; i++) { - NativeLibrary lib = libs.elementAt(i); - long entry = lib.find(name); - if (entry != 0) - return entry; + /* + * Returns the native libraries map associated with this class loader + * This method will create the map at the first time when called. + */ + private Map nativeLibraries() { + Map libs = nativeLibraries; + if (libs == null) { + synchronized (loadedLibraryNames) { + libs = nativeLibraries; + if (libs == null) { + libs = nativeLibraries = new ConcurrentHashMap<>(); + } } } - return 0; + return libs; } - // -- Assertion management -- final Object assertionLock;