JDK8: Unloading native JNI library
David Holmes
david.holmes at oracle.com
Fri Aug 25 02:18:46 UTC 2017
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