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?
In Clojure, copy/pastable at the REPL:
(-> (doto (.getDeclaredField ClassLoader "loadedLibraryNames")
(.setAccessible true))
(.get (ClassLoader/getSystemClassLoader)))
as of Janurary 2019, the correct answer (from jdk9+ ondwards) is: There is no way anymore to get the List of loaded libraries.
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:
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.
Since Nicolas mentioned Scala, here is one way to do jitter's solution via JRuby (tested in 1.6 and 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
In Groovy (tested in 2.3.3):
libs = ClassLoader.class.getDeclaredField("loadedLibraryNames")
libs.setAccessible(true)
libraries = libs.get(ClassLoader.getSystemClassLoader())
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.
First the simplified version with no bu....it exception handling, nice error messages, 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á libraries
holds the names of the loaded native libraries.
Get the full source code from here
I've built on top of jitter's solution. This allows you to find out who (ClassLoader, Class) loaded each native library.
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;
}
}