正如主题所说,Java中有没有办法获取在任何给定时间加载的所有JNI本机库的列表?
7 回答
There is a way to determine all currently loaded native libraries if you meant that. Already unloaded libraries can't be determined.
Based on the work of Svetlin Nakov (Extract classes loaded in JVM to single JAR) I did a POC which gives you the names of the loaded native libraries from the application classloader and the classloader of the current class.
First the simplified version with no bu....it exception handling, nice error messages, javadoc, ....
Get the private field in which the class loader stores the already loaded libraries via reflection
public class ClassScope {
private static final java.lang.reflect.Field LIBRARIES;
static {
LIBRARIES = ClassLoader.class.getDeclaredField("loadedLibraryNames");
LIBRARIES.setAccessible(true);
}
public static String[] getLoadedLibraries(final ClassLoader loader) {
final Vector<String> libraries = (Vector<String>) LIBRARIES.get(loader);
return libraries.toArray(new String[] {});
}
}
Call the above like this
final String[] libraries = ClassScope.getLoadedClasses(ClassLoader.getSystemClassLoader()); //MyClassName.class.getClassLoader()
And voilá libraries
holds the names of the loaded native libraries.
Get the full source code from here
我建立在jitter 的解决方案之上。这使您可以找出谁(ClassLoader,Class)加载了每个本机库。
import java.lang.reflect.Field;
import java.util.*;
import java.util.Map.Entry;
/**
* Helper functions for native libraries.
* <p/>
* @author Gili Tzabari
*/
public class NativeLibraries
{
private final Field loadedLibraryNames;
private final Field systemNativeLibraries;
private final Field nativeLibraries;
private final Field nativeLibraryFromClass;
private final Field nativeLibraryName;
/**
* Creates a new NativeLibraries.
* <p/>
* @throws NoSuchFieldException if one of ClassLoader's fields cannot be found
*/
public NativeLibraries() throws NoSuchFieldException
{
this.loadedLibraryNames = ClassLoader.class.getDeclaredField("loadedLibraryNames");
loadedLibraryNames.setAccessible(true);
this.systemNativeLibraries = ClassLoader.class.getDeclaredField("systemNativeLibraries");
systemNativeLibraries.setAccessible(true);
this.nativeLibraries = ClassLoader.class.getDeclaredField("nativeLibraries");
nativeLibraries.setAccessible(true);
Class<?> nativeLibrary = null;
for (Class<?> nested : ClassLoader.class.getDeclaredClasses())
{
if (nested.getSimpleName().equals("NativeLibrary"))
{
nativeLibrary = nested;
break;
}
}
this.nativeLibraryFromClass = nativeLibrary.getDeclaredField("fromClass");
nativeLibraryFromClass.setAccessible(true);
this.nativeLibraryName = nativeLibrary.getDeclaredField("name");
nativeLibraryName.setAccessible(true);
}
/**
* Returns the names of native libraries loaded across all class loaders.
* <p/>
* @return a list of native libraries loaded
*/
public List<String> getLoadedLibraries()
{
try
{
@SuppressWarnings("UseOfObsoleteCollectionType")
final Vector<String> result = (Vector<String>) loadedLibraryNames.get(null);
return result;
}
catch (IllegalArgumentException | IllegalAccessException e)
{
throw new AssertionError(e);
}
}
/**
* Returns the native libraries loaded by the system class loader.
* <p/>
* @return a Map from the names of native libraries to the classes that loaded them
*/
public Map<String, Class<?>> getSystemNativeLibraries()
{
try
{
Map<String, Class<?>> result = new HashMap<>();
@SuppressWarnings("UseOfObsoleteCollectionType")
final Vector<Object> libraries = (Vector<Object>) systemNativeLibraries.get(null);
for (Object nativeLibrary : libraries)
{
String libraryName = (String) nativeLibraryName.get(nativeLibrary);
Class<?> fromClass = (Class<?>) nativeLibraryFromClass.get(nativeLibrary);
result.put(libraryName, fromClass);
}
return result;
}
catch (IllegalArgumentException | IllegalAccessException e)
{
throw new AssertionError(e);
}
}
/**
* Returns a Map from the names of native libraries to the classes that loaded them.
* <p/>
* @param loader the ClassLoader that loaded the libraries
* @return an empty Map if no native libraries were loaded
*/
public Map<String, Class<?>> getNativeLibraries(final ClassLoader loader)
{
try
{
Map<String, Class<?>> result = new HashMap<>();
@SuppressWarnings("UseOfObsoleteCollectionType")
final Vector<Object> libraries = (Vector<Object>) nativeLibraries.get(loader);
for (Object nativeLibrary : libraries)
{
String libraryName = (String) nativeLibraryName.get(nativeLibrary);
Class<?> fromClass = (Class<?>) nativeLibraryFromClass.get(nativeLibrary);
result.put(libraryName, fromClass);
}
return result;
}
catch (IllegalArgumentException | IllegalAccessException e)
{
throw new AssertionError(e);
}
}
/**
* The same as {@link #getNativeLibraries()} except that all ancestor classloaders are processed
* as well.
* <p/>
* @param loader the ClassLoader that loaded (or whose ancestors loaded) the libraries
* @return an empty Map if no native libraries were loaded
*/
public Map<String, Class<?>> getTransitiveNativeLibraries(final ClassLoader loader)
{
Map<String, Class<?>> result = new HashMap<>();
ClassLoader parent = loader.getParent();
while (parent != null)
{
result.putAll(getTransitiveNativeLibraries(parent));
parent = parent.getParent();
}
result.putAll(getNativeLibraries(loader));
return result;
}
/**
* Converts a map of library names to the classes that loaded them to a map of library names to
* the classloaders that loaded them.
* <p/>
* @param libraryToClass a map of library names to the classes that loaded them
* @return a map of library names to the classloaders that loaded them
*/
public Map<String, ClassLoader> getLibraryClassLoaders(Map<String, Class<?>> libraryToClass)
{
Map<String, ClassLoader> result = new HashMap<>();
for (Entry<String, Class<?>> entry : libraryToClass.entrySet())
result.put(entry.getKey(), entry.getValue().getClassLoader());
return result;
}
/**
* Returns a list containing the classloader and its ancestors.
* <p/>
* @param loader the classloader
* @return a list containing the classloader, its parent, and so on
*/
public static List<ClassLoader> getTransitiveClassLoaders(ClassLoader loader)
{
List<ClassLoader> result = new ArrayList<>();
ClassLoader parent = loader.getParent();
result.add(loader);
while (parent != null)
{
result.add(parent);
parent = parent.getParent();
}
return result;
}
}
FWIW,这里又是jitter 的解决方案,这次是一个小的 Scala 方法:
def loadedLibs: Seq[String] = {
val libs = classOf[ClassLoader].getDeclaredField("loadedLibraryNames")
libs.setAccessible(true)
import scala.collection.JavaConverters._
libs.get(ClassLoader.getSystemClassLoader())
.asInstanceOf[java.util.Vector[String]]
.asScala
}
由于 Nicolas 提到了 Scala,这里有一种通过 JRuby 来解决抖动的方法(在 1.6 和 1.7 中测试):
require 'java'
import 'java.lang.ClassLoader'
f = ClassLoader.java_class.declared_field('loadedLibraryNames')
f.accessible = true
f.value(ClassLoader.system_class_loader).to_array.to_a
在 Clojure 中,复制/粘贴到 REPL:
(-> (doto (.getDeclaredField ClassLoader "loadedLibraryNames")
(.setAccessible true))
(.get (ClassLoader/getSystemClassLoader)))
在 Groovy 中(在 2.3.3 中测试):
libs = ClassLoader.class.getDeclaredField("loadedLibraryNames")
libs.setAccessible(true)
libraries = libs.get(ClassLoader.getSystemClassLoader())
截至 2019 年 1 月,正确答案(从 jdk9+ 开始)是:无法再获取已加载库的列表。
尽管提到的字段 ( loadedLibraryNames
) 仍然存在于hotspot
-type VM 中,但它不存在于其他 (如openj9
) 中。此外,如果您在 jdk9 及更高版本上尝试此操作,您将在终端上收到警告,该访问权限将在 Java 12 及更高版本中被撤销:
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by <class> (file:<file>) to method|constructor
WARNING: Please consider reporting this to the maintainers of <file>
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
也就是说,* 此方法仅适用于非常特定的 JVM。不要依赖这一点,因为有人可能会使用另一个成熟的 VM,如openj9
, azul
,corretto
等。 * 从 Java 9 正式开始它就无法工作,但会从 Java 12 开始使你的 JVM 崩溃(或产生意外的输出)。