【问题标题】:How do I get a list of JNI libraries which are loaded?如何获取已加载的 JNI 库列表?
【发布时间】:2022-05-11 04:00:55
【问题描述】:

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

【问题讨论】:

    标签: java java-native-interface


    【解决方案1】:

    如果您的意思是这样,有一种方法可以确定所有当前加载的本机库。无法确定已卸载的库。

    基于 Svetlin Nakov (Extract classes loaded in JVM to single JAR) 的工作,我做了一个 POC,它为您提供了从应用程序类加载器和当前类的类加载器加载的本机库的名称。

    第一个简化版本没有 bu....it 异常处理、漂亮的错误消息、javadoc、....

    通过反射获取类加载器存储已加载库的私有字段

    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[] {});
        }
    }
    

    像这样调用上面的代码

    final String[] libraries = ClassScope.getLoadedClasses(ClassLoader.getSystemClassLoader()); //MyClassName.class.getClassLoader()
    

    瞧,libraries 保存了加载的本机库的名称。

    here获取完整源代码

    【讨论】:

    • 谢谢!我试过这个并得到这个错误消息: java.lang.RuntimeException: java.lang.IllegalAccessException: Class net.grow.web.ClassScope can't access a member of class java.lang.ClassLoader with modifiers "private static"
    • 我正在使用 Android Studio,我得到一个“java.lang.RuntimeException: ClassScope::getLoadedLibraries() cannot be used in this JRE”。对此有任何想法吗?
    • +1 - 请注意,至少在最新版本的 Java 中,loadedLibraryNames 是静态成员,因此 loader arg 到 LIBRARIES.get(loader) 被忽略。
    • No field loadedLibraryNames in class Ljava/lang/ClassLoader;
    【解决方案2】:

    我建立在jitter's solution 之上。这使您可以找出谁(ClassLoader、Class)加载了每个本机库。

    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Vector;
    
    /**
     * 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;
        }
    }
    

    【讨论】:

    • 你有一个如何使用这个类的例子吗? new NativeLibraries() 失败并显示 Exception in thread "main" java.lang.NoSuchFieldException: loadedLibraryNames
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      FWIW,这里又是jitter's solution,这次是一个小的 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
      }
      

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

          在 Groovy 中(在 2.3.3 中测试):

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

          【讨论】:

          • 对于那些因为顶部的 Groovy 而避免这个答案的人,它在 java 中工作得很好,你只需将它全部包装在一个 try 块中并捕获一些异常。 libs.get get 返回一个 Set ,其中包含当时加载的所有 dll(我假设由给定的类加载器)
          【解决方案7】:

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

          虽然提到的字段 (loadedLibraryNames) 仍然存在于 hotspot 类型的 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,例如 openj9azulcorretto 等。 * 从 Java 9 正式开始它不会工作,但会从 Java 12 开始使你的 JVM 崩溃(或产生意外的输出)。

          【讨论】:

          • 您将能够使用 VM 的 --add-opens 参数修复警告,因此这部分没什么大不了的。 stackoverflow.com/a/46230678/733092你怎么知道那个字段被删除了?我试着用谷歌搜索它。
          猜你喜欢
          • 1970-01-01
          • 2015-06-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-11
          • 2014-04-14
          • 1970-01-01
          相关资源
          最近更新 更多