JDK8: Unloading native JNI library

Vemund Ostgaard vemund.ostgaard at oracle.com
Fri Aug 25 13:48:09 UTC 2017


Hi David,

On 25. aug. 2017 04:18, David Holmes wrote:
> Hi Vemund,
>
> What OS do you see this on? Are you able to test different OS?

Excellent question. Earlier in my struggles to get this working I had 
tested on both an RHEL7 and Ubuntu 16.04 and seen the same problems, so 
I did further testing only on the Ubuntu machine. Since I resolved a few 
issues since then, I went back to RHEL7 and rebuilt everything there to 
confirm both OS were still giving the same behavior.

Turned out that on RHEL7 I did no longer see any issue, the unloading 
worked fine also on the first attempt. To be sure, I rebuilt the native 
library from scratch also on Ubuntu 16.04, and to my surprise the issue 
was gone there too.

Scratching my head I eventually worked out how I had hit the problem 
originally. At some point I must have mixed up the native library 
compiled on RHEL7 and the one compiled on Ubuntu by mistake. I can now 
only reproduce the issue if I try to load the native library compiled on 
RHEL7 on the Ubuntu 16.04 machine. In that case, everything behaved 
exactly as before, the first library loaded can not be unloaded, but 
subsequent libraries can.

So, it is all a mistake on my part basically, when loading and unloading 
the native libraries on the same Linux flavor and version they were 
compiled on, it works.

Thanks for the reply, it led me to finding my mistake.

Vemund

>
> Thanks,
> David
>
> On 24/08/2017 10:37 PM, Vemund Ostgaard wrote:
>> Hello,
>>
>> I am struggling with a problem related to unloading (and reloading) a
>> native JNI library that has been loaded with System.load(). I have
>> been referred to this mailing list as the right place to ask, so
>> seeking your advice on the issue.
>>
>> My goal was to load a JNI library from a JVM process and be able to
>> reload an updated copy of the same library later without restarting
>> the JVM. The library might be loaded from the exact same path as
>> before or from a different path.
>>
>> The testing I have done (more details below) indicates to me that the
>> first JNI library I load with System.load() does not get unloaded
>> completely when its loading classloader is garbage collected,
>> subsequent attempts to load a library from the same path indicates
>> that the native library kept the state it had from the first loaded copy.
>>
>> Surprisingly though, any other copy of the native library that is
>> loaded from other file system locations than the first path are
>> apparently unloaded when their loading classloader is garbage
>> collected, and subsequent attempts to load from those paths indicate
>> that those libraries do not keep their state.
>>
>> So, I am curious whether there is something special about the first
>> time System.load() is used from application code in a new JVM, and
>> whether there is some way I can avoid that behavior.
>>
>>
>> Details about my testing:
>>
>> I have added these methods to my native JNI library:
>>
>> JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
>> {
>>      counter++;
>>      printf("JNI_OnLoad called: %d\n", counter);
>>      (void)jvm;
>>      (void)reserved;
>>      return JNI_VERSION_1_4;
>> }
>>
>> JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *jvm, void *reserved)
>> {
>>      printf( "JNI_OnUnload called: %d\n", counter);
>>      (void)jvm;
>>      (void)reserved;
>>      return;
>> }
>>
>> The counter is a global variable in the same file, and it is
>> initialized to 0. The plan was to see if the counter was initialized
>> to 0 again after the library is unloaded and loaded again, and also to
>> see that these methods were called at all. I also recompiled the
>> native library in two other versions with the counter initialized to
>> 100 and 200 respectively, to be able to tell the difference between
>> different copies of the library being loaded/unloaded.
>>
>> I wrote a small java class that calls System.load() to load my native
>> JNI library, and a small custom classloader that extends URL
>> classloader. To both of these classes I added overrides for finalize()
>> just to see it being called and printing out a unique ID to know what
>> copy of each class was being finalized.
>>
>> The third Java class is just a main() method that calls a test()
>> method with the test logic using the other two classes to test loading
>> and unloading of the native library. When the test() method returns to
>> the main() method, the classloader object goes out of scope and I call
>> System.gc() a few times to get everything garbage collected. The
>> test() method also returns a WeakReference() to the classloader object
>> so I can call WeakReference.get() before and after the System.gc()
>> calls to verify that this becomes null. I interpret the WeakReference
>> becoming null as a sign that my classloader is getting garbage collected.
>>
>> My testing shows that in all cases:
>> - JNI_OnLoad() and JNI_OnUnload() gets called as expected
>> - The finalizer() methods for my two classes are called as expected
>> - The WeakReference.get() for my custom classloader returns null after
>> a few System.gc() calls and sleeping a couple seconds total.
>>
>> As already mentioned, the first JNI library loaded keeps its state,
>> the counter printed from JNI_OnLoad() and JNI_OnUnload() keeps
>> increasing for every repeated load+unload from that file location,
>> while for all other libraries loaded from other file locations, the
>> state is apparently reset. This holds true also if using multiple
>> custom classloaders or identical copies of the JNI library in
>> different locations. The key factor seems to only be loading from the
>> same file location as used the first time.
>>
>> I can't see that my code behaves any different for the multiple times
>> I try to load and unload the JNI libraries, so I am wondering if there
>> is some JNI bug or known behavior that explains what I see?
>>
>>
>> Thank you,
>> Vemund



More information about the core-libs-dev mailing list