Как получить список загруженных библиотек JNI?

Как говорится в теме, есть ли способ в Java получить список всех собственных библиотек JNI, которые были загружены в любой момент времени?


person benhsu    schedule 17.06.2009    source источник


Ответы (7)


Есть способ определить все загруженные на данный момент собственные библиотеки, если вы это имели в виду. Уже выгруженные библиотеки не могут быть определены.

На основе работы Светлина Накова (Извлечь классы, загруженные в JVM, в один JAR) Я сделал POC, который дает вам имена загруженных собственных библиотек из загрузчика классов приложения и загрузчика классов текущего класса.

Сначала упрощенная версия без встроенной обработки исключений, приятных сообщений об ошибках, 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 содержит имена загруженных собственных библиотек.

Получите полный исходный код здесь

person jitter    schedule 17.06.2009
comment
Спасибо! Я попробовал это и получил это сообщение об ошибке: java.lang.RuntimeException: java.lang.IllegalAccessException: Class net.grow.web.ClassScope не может получить доступ к члену класса java.lang.ClassLoader с модификаторами private static - person benhsu; 17.06.2009
comment
Попробуйте добавить LIBRARIES.setAccessible(true); после вызова getDeclaredField, если у вас его еще нет. Если это все еще не удается, то либо ваша JVM не совместима с SUN, либо установлен SecurityManager, который не позволяет вам использовать механизм отражения. - person jitter; 18.06.2009
comment
Последней идеей (если применимо к вашей среде) было бы написать свой собственный ClassLoader в качестве адаптера объекта для обычного ClassLoader. И там реализовать какое-то логирование вызовов load и loadLibrary - person jitter; 18.06.2009
comment
Не будет ли кто-нибудь так любезен, чтобы повторно разместить код с сайта загрузки в PasteBin или какой-либо другой системе загрузки без рекламы? На самом деле я не могу получить доступ к этому сайту загрузки на работе. - person Salim Fadhley; 28.11.2011
comment
Вот: AllLoadedNativeLibrariesInJVM.java и ClassScope.java - person jitter; 30.11.2011
comment
Я использую Android Studio и получаю исключение java.lang.RuntimeException: ClassScope::getLoadedLibraries() нельзя использовать в этой JRE. Любая идея об этом? - person gts13; 21.01.2015
comment
+1. Обратите внимание, что в последней версии Java, по крайней мере, loadedLibraryNames является статическим членом, поэтому аргумент loader для LIBRARIES.get(loader) игнорируется. - person sfjac; 12.02.2016

Я построил поверх решение джиттера. Это позволяет узнать, кто (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;
    }
}
person Gili    schedule 03.11.2011

Поскольку Николас упомянул 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
person Gary S. Weaver    schedule 30.01.2013

FWIW, вот дрожание решение снова, на этот раз в виде небольшого метода 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
}
person Nicolas Payette    schedule 21.01.2013

В Clojure скопируйте/вставьте в REPL:

(-> (doto (.getDeclaredField ClassLoader "loadedLibraryNames")
      (.setAccessible true))
    (.get (ClassLoader/getSystemClassLoader)))
person Gary Verhaegen    schedule 11.05.2014

В Groovy (проверено в версии 2.3.3):

libs = ClassLoader.class.getDeclaredField("loadedLibraryNames")
libs.setAccessible(true)
libraries = libs.get(ClassLoader.getSystemClassLoader())
person sheiden    schedule 15.12.2016
comment
Для тех, кто избегает этого ответа из-за Groovy вверху, он отлично работает в java, вам просто нужно обернуть все это в блок try и поймать некоторые исключения. libs.get возвращает Set‹String› со всеми DLL, загруженными в то время (я предполагаю, что данный загрузчик классов) - person Gustavo Ulises Arias Méndez; 27.12.2018

по состоянию на январь 2019 года правильный ответ (начиная с jdk9+ и далее): больше нет способа получить список загруженных библиотек.

Хотя указанное поле (loadedLibraryNames) по-прежнему существует в виртуальных машинах типа hotspot, его нет в других (например, 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. не полагайтесь на это, так как кто-то может использовать другую зрелую виртуальную машину, такую ​​​​как openj9, azul, corretto и т. д. * Это не будет работать, начиная с Java 9 официально, но приведет к сбою вашей JVM (или даст неожиданный результат), начиная с Java 12.

person Ben    schedule 28.03.2019
comment
Вы сможете исправить предупреждение с помощью аргумента --add-opens для вашей виртуальной машины, так что эта часть не имеет большого значения. stackoverflow.com/a/46230678/733092 Как узнать, что это поле удаляется? Я попробовал погуглить. - person Noumenon; 14.01.2020