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

2022-09-01 22:02:33

正如主题所说,在Java中是否有一种方法可以获得在任何给定时间加载的所有JNI本机库的列表?


答案 1

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

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

首先是没有bu的简化版本....它异常处理,漂亮的错误消息,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()

voilá保存加载的本机库的名称。

此处获取完整的源代码


答案 2

我建立在抖动解决方案之上。这使您可以找出谁(类加载器,类)加载了每个本机库。

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

推荐