Как говорится в теме, есть ли способ в Java получить список всех собственных библиотек JNI, которые были загружены в любой момент времени?
Как получить список загруженных библиотек JNI?
Ответы (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
содержит имена загруженных собственных библиотек.
Получите полный исходный код здесь
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;
}
}
Поскольку Николас упомянул 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
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
}
В 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 года правильный ответ (начиная с 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.
--add-opens
для вашей виртуальной машины, так что эта часть не имеет большого значения. stackoverflow.com/a/46230678/733092 Как узнать, что это поле удаляется? Я попробовал погуглить.
- person Noumenon; 14.01.2020