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