< prev index next >

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

Print this page

        

@@ -35,34 +35,37 @@
 import java.security.AccessControlContext;
 import java.security.CodeSource;
 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;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
 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;
 import sun.security.util.SecurityConstants;
 

@@ -2373,76 +2376,164 @@
      *
      * @see      ClassLoader
      * @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<String, NativeLibrary> 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");
                     }
-                    /* unload the library. */
-                    ClassLoader.nativeLibraryContext.push(this);
+
+                /*
+                 * 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");
+                        }
+                    }
+                }
+                NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin);
+                // load the native library
+                nativeLibraryContext.push(lib);
                     try {
-                        unload(name, isBuiltin);
+                    if (!lib.load()) return false;
                     } finally {
-                        ClassLoader.nativeLibraryContext.pop();
-                    }
+                    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;
         }
+
+        // native libraries being loaded
+        static Deque<NativeLibrary> nativeLibraryContext = new LinkedList<>();
+
+        /*
+         * 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);
     }
 
-    // All native library names we've loaded.
-    private static Vector<String> loadedLibraryNames = new Vector<>();
+                this.name = name;
+                this.handle = handle;
+                this.isBuiltin = isBuiltin;
+            }
 
-    // Native libraries belonging to system classes.
-    private static Vector<NativeLibrary> systemNativeLibraries
-        = new Vector<>();
+            @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();
+                    }
 
-    // Native libraries associated with the class loader.
-    private Vector<NativeLibrary> nativeLibraries = new Vector<>();
+                }
+            }
+        }
 
-    // native libraries being loaded/unloaded.
-    private static Stack<NativeLibrary> nativeLibraryContext = new Stack<>();
+        // 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[];
     private static String sys_paths[];
 

@@ -2547,14 +2638,15 @@
                     return;
                 }
             }
         }
         // 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
         String name = findBuiltinLib(file.getName());
         boolean isBuiltin = (name != null);

@@ -2571,90 +2663,69 @@
                 });
             if (name == null) {
                 return false;
             }
         }
-        ClassLoader loader =
-            (fromClass == null) ? null : fromClass.getClassLoader();
-        Vector<NativeLibrary> 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);
             }
 
-            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.
+    /*
+     * Invoked in the VM class linking code.
                  */
-                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");
-                        }
-                    }
-                }
-                NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin);
-                nativeLibraryContext.push(lib);
-                try {
-                    lib.load(name, isBuiltin);
-                } finally {
-                    nativeLibraryContext.pop();
+    private static long findNative(ClassLoader loader, String name) {
+        Map<String, NativeLibrary> 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;
                 }
-                if (lib.loaded) {
-                    loadedLibraryNames.addElement(name);
-                    libs.addElement(lib);
-                    return true;
+        return 0;
                 }
-                return false;
+
+    // All native library names we've loaded.
+    // This also serves as the lock to obtain nativeLibraries
+    // and write to nativeLibraryContext.
+    private static final Set<String> loadedLibraryNames = new HashSet<>();
+
+    // Native libraries belonging to system classes.
+    private static Map<String, NativeLibrary> systemNativeLibraries = null;
+
+    // Native libraries associated with the class loader.
+    private Map<String, NativeLibrary> nativeLibraries = null;
+
+    /*
+     * 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<String, NativeLibrary> systemNativeLibraries() {
+        synchronized (loadedLibraryNames) {
+            if (systemNativeLibraries == null) {
+                systemNativeLibraries = new ConcurrentHashMap<>();
             }
+            return systemNativeLibraries;
         }
     }
 
-    // Invoked in the VM class linking code.
-    static long findNative(ClassLoader loader, String name) {
-        Vector<NativeLibrary> 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<String, NativeLibrary> nativeLibraries() {
+        synchronized (loadedLibraryNames) {
+            if (nativeLibraries == null) {
+                nativeLibraries = new ConcurrentHashMap<>();
             }
+            return nativeLibraries;
         }
-        return 0;
     }
 
-
     // -- Assertion management --
 
     final Object assertionLock;
 
     // The default toggle for assertion checking.
< prev index next >