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?
7 Answers
[DISCLAIMER: Note that this solution always was hackish. And in most cases won't work anymore nowadays. Check Benjamins answer for more info]
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.

- 53,475
- 11
- 111
- 124
-
Thanks! I tried this and got this error message: java.lang.RuntimeException: java.lang.IllegalAccessException: Class net.grow.web.ClassScope can not access a member of class java.lang.ClassLoader with modifiers "private static" – benhsu Jun 17 '09 at 18:46
-
Try adding LIBRARIES.setAccessible(true); after the call to getDeclaredField if you don't have that already. If it still fails then either your JVM is not SUN compatible or there is a SecurityManager in place which prevents you from using the reflection mechanism – jitter Jun 17 '09 at 21:16
-
A last idea (if applicable to your environment) would be to write your own ClassLoader as an Object Adapter for the normal ClassLoader. And there implement some kind of "logging" for the calls to load and loadLibrary – jitter Jun 17 '09 at 22:31
-
Would anybody be so kind as to re-host the code from the download site onto PasteBin or some other ad-free download system? I'm not actually able to access that download site at work. – Salim Fadhley Nov 28 '11 at 15:29
-
1Here you go: [AllLoadedNativeLibrariesInJVM.java](http://pastebin.com/aDgGqjEr) and [ClassScope.java](http://pastebin.com/eVXFdgr9) – jitter Nov 30 '11 at 18:37
-
1I am using Android Studio and I get a "java.lang.RuntimeException: ClassScope::getLoadedLibraries() cannot be used in this JRE". Any idea about this? – gts13 Jan 21 '15 at 10:33
-
1+1 - Note that in recent version of Java, at least, `loadedLibraryNames` is a static member, so the `loader` arg to `LIBRARIES.get(loader)` is ignored. – sfjac Feb 11 '16 at 22:44
-
1`No field loadedLibraryNames in class Ljava/lang/ClassLoader;` – Iman Marashi Sep 17 '21 at 15:53
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.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;
}
}

- 10,029
- 11
- 83
- 152

- 86,244
- 97
- 390
- 689
-
Do you have an example how to use this class? `new NativeLibraries()` fails with `Exception in thread "main" java.lang.NoSuchFieldException: loadedLibraryNames` – Eric Duminil Nov 03 '21 at 09:15
-
FWIW, here's jitter's solution again, this time as a small Scala method:
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
}

- 1
- 1

- 14,847
- 1
- 27
- 37
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

- 7,966
- 4
- 37
- 61
In Clojure, copy/pastable at the REPL:
(-> (doto (.getDeclaredField ClassLoader "loadedLibraryNames")
(.setAccessible true))
(.get (ClassLoader/getSystemClassLoader)))

- 1,081
- 9
- 6
In Groovy (tested in 2.3.3):
libs = ClassLoader.class.getDeclaredField("loadedLibraryNames")
libs.setAccessible(true)
libraries = libs.get(ClassLoader.getSystemClassLoader())

- 11
- 2
-
For those who avoid this answer because of the Groovy at the top, it works in java just fine, you just have to wrap it all in a try block and catch some exceptions. The libs.get get returns a Set
with all the dlls loaded at the time (I assume by the given class loader) – Gustavo Ulises Arias Méndez Dec 27 '18 at 18:17
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.

- 1,173
- 1
- 13
- 36
-
You will be able to fix the warning with an `--add-opens` argument to your VM, so that part's not a big deal. https://stackoverflow.com/a/46230678/733092 How do you know that field is being removed? I tried Googling it. – Noumenon Jan 13 '20 at 23:40