Java 如何获取加载的 JNI 库列表?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1007861/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 22:12:47  来源:igfitidea点击:

How do I get a list of JNI libraries which are loaded?

javajava-native-interface

提问by benhsu

Just what the subject says, is there a way in Java to get a list of all the JNI native libraries which have been loaded at any given time?

正如主题所说,Java 中有没有办法获取在任何给定时间加载的所有 JNI 本机库的列表?

回答by jitter

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.

基于 Svetlin Nakov(将 JVM 中加载的类提取到单个 JAR)的工作,我做了一个 POC,它为您提供了从应用程序类加载器和当前类的类加载器加载的本地库的名称。

First the simplified version with no bu....it exception handling, nice error messages, javadoc, ....

首先是没有BU的简化版本......它异常处理,漂亮的错误消息,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á librariesholds the names of the loaded native libraries.

voilálibraries包含加载的本地库的名称。

Get the full source code from here

这里获取完整的源代码

回答by Gili

I've built on top of jitter's solution. This allows you to find out who (ClassLoader, Class) loaded each native library.

我建立在抖动的解决方案之上。这使您可以找出谁(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;
    }
}

回答by Nicolas Payette

FWIW, here's jitter's solutionagain, this time as a small Scala method:

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
}

回答by Gary S. Weaver

Since Nicolas mentioned Scala, here is one way to do jitter's solution via JRuby (tested in 1.6 and 1.7):

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

回答by Gary Verhaegen

In Clojure, copy/pastable at the REPL:

在 Clojure 中,在 REPL 上复制/粘贴:

(-> (doto (.getDeclaredField ClassLoader "loadedLibraryNames")
      (.setAccessible true))
    (.get (ClassLoader/getSystemClassLoader)))

回答by sheiden

In Groovy (tested in 2.3.3):

在 Groovy 中(在 2.3.3 中测试):

libs = ClassLoader.class.getDeclaredField("loadedLibraryNames")
libs.setAccessible(true)
libraries = libs.get(ClassLoader.getSystemClassLoader())

回答by Ben

as of Janurary 2019, the correct answer (from jdk9+ ondwards) is: There is no way anymore to get the List of loaded libraries.

截至 2019 年 1 月,正确答案(从 jdk9+ 开始)是:再也无法获取已加载库的列表。

Although the mentioned field (loadedLibraryNames) still exists in hotspot-type VMs, it does not exist in others (like openj9). Also, if you try this on jdk9 onwards, you will get a warning on your terminal, that this access will be revoked in Java 12 onwards:

尽管提到的字段 ( loadedLibraryNames) 仍然存在于hotspot-type VM 中,但它不存在于其他类型的 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

That said, * this method only works on a very specific JVM. do not rely on this, as someone may use another mature VM like openj9, azul, corretto, etc. * It won't work since Java 9 officially, but will crash your JVM (or yield unexpected output) starting from Java 12.

也就是说,* 这种方法仅适用于非常特定的 JVM。不要依赖于此,因为有人可能会使用另一个成熟的 VM,例如openj9azulcorretto等。 * 从 Java 9 正式开始它就无法工作,但会从 Java 12 开始使您的 JVM 崩溃(或产生意外输出)。