Wednesday, September 14, 2011

java.lang.UnsatisfiedLinkError: Native Library HelloWorld.dll already loaded in another classloader

Continuation of the previous post.


Some workarounds to overcome the problem are:

1) Load the dll in system classloader that is available to all child
classloaders
2) Always load the dll with a different name - Bad approach
3) Unload DLL by cleaning up ClassLoader cache. I tried this approach after some analysis as option1 was not feasible in my case.

Whenever a native library is loaded in a classloader, the following private static fields get updated - loadedLibraryNames and nativeLibraries.
Refer java.util.ClassLoader javadocs for more info on these fileds.

java.util.ClassLoader creates a ClassLoader$NativeLibrary(local inner class) class object
for each and every native library it loads and also maintains a cache containing the names of the DLL files that are loaded. 
  
I have used reflection to fetch the fields - loadedLibraryNames and nativeLibraries - from ClassLoader and cleared all the entries corresponding to the DLL that i wanted to unload.
This solution worked in my case and I need not wait till the ClassLoader is garbage collected inorder to call a native method from different classloaders.

The complete source code can be downloaded from 
  
https://docs.google.com/viewer?a=v&pid=explorer&chrome=true&srcid=0BxW5CDXA1SsmNGY3NWE5MmItZDJkMC00MTVlLTg0ZjQtZmI3ODIzY2NmZjQ3&hl=en_US

Phani Kumar Bandanakanti: java.lang.UnsatisfiedLinkError: Native Library Hel...

Phani Kumar Bandanakanti: java.lang.UnsatisfiedLinkError: Native Library Hel...: Problem: There's no way to unload a dll loaded by a classloader. DLL will be unloaded only during classloader garbage collection. Also a d...

java.lang.UnsatisfiedLinkError: Native Library HelloWorld.dll already loaded in another classloader

Problem:

There's no way to unload a dll loaded by a classloader. DLL will be unloaded only during classloader garbage collection.
Also a dll cannot be loaded more than once in a jvm even if we use distinct classloaders.

This is a known issue in JDK and more information can be found in the links below.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4225434
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5075039

Update from bugs.sun.com

A native library can not be loaded into two live ClassLoaders objects
at the same time and the reasoning for this is obvious -- native code
that caches JNI IDs will get wrong answers.  The long term fix for
this is a way for the native library to tell the VM (on JNI_OnLoad)
that it is multiple-ClassLoader safe.  That would be an RFE.

However when the ClassLoader has died, the ClassLoader.NativeLibrary
finalizer could unload the library.  Currently we do not do this
and this is intentional -- unloading libraries is an MT unsafe
operation on NT and there are troubles with runFinalizersOnExit.
A workaround would be to have the class that loads a native library
be loaded by a shared classloader (through the parenting mechanism of
ClassLoaders in 1.2).

Sample code to reproduce the problem:

1) Created a class HelloWorld.java that declares a native method.
2) Generated the C header file, wrote the C implementation and created a DLL file. All these steps are clearly explained in the following link.
http://java.sun.com/docs/books/jni/html/start.html
3) To reproduce the problem, i wrote a simple java class which loads the DLL file and calls the native method multiple (three) times.
Each time it tries to load the native library and calls the native method in a different classloader.
The code snippet is shown in the image below.
Complete codesnippets and workarounds to overcome the problem will be provided in the next post.