JDK8: Unloading native JNI library

David Holmes david.holmes at oracle.com
Fri Aug 25 02:33:20 UTC 2017


And what is the name of the library?

Thanks,
David

On 25/08/2017 12:18 PM, David Holmes wrote:
> Hi Vemund,
> 
> What OS do you see this on? Are you able to test different OS?
> 
> 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