< 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 >