February 19, 2006

JNI_OnLoad 的非預期現象

Alan Burlison 在 blog [JNI_OnUnload(): mostly useless] 提到 JNI_Unload 這個 JNI call 搭配 JNIInvokeInterface 的 AttachCurrentThread call 造成非預期的現象。在 kaffe/include/jni.h 中,相關的定義如下:
    /* These functions might be defined in libraries
       which we load; the JNI implementation calls
       them at the appropriate times.  */
    extern JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM *, void *);
    extern JNIEXPORT void JNICALL JNI_OnUnload (JavaVM *, void *);
     * Invocation API.
    struct JNIInvokeInterface
      void *reserved0;
      void *reserved1;
      void *reserved2;
      jint (JNICALL *DestroyJavaVM)         (JavaVM *);
      jint (JNICALL *AttachCurrentThread)   (JavaVM *, void **, void *);
      jint (JNICALL *DetachCurrentThread)   (JavaVM *);
      jint (JNICALL *GetEnv)                (JavaVM *, void **, jint);
      jint (JNICALL *AttachCurrentThreadAsDaemon) (JavaVM *, void **, void *);
Alan Burlison 指出問題的癥結:
    what was puzzling me was that although JNI_OnLoad() was being called OK, JNI_OnUnload() was never being called, and because the thread was attached to the JVM via a call to AttachCurrentThread(), the JVM would never exit as it still had an active thread. A simple workaround was to attach the thread to the JVM as a daemon thread using AttachCurrentThreadAsDaemon as daemon threads don't keep the JVM alive, but that still meant the DLL wasn't being cleaned up properly.
稍後他試著解決問題,過去可用 java.lang.System.runFinalizersOnExit() method,不過這已經 deprecated,他最後使用的解法如下:
    In light of this it appears that although JNI_OnLoad() is useful, JNI_OnUnload() is less than useful. There is a workaround, which is to register a native exit callback using Runtime.addShutdownHook() and do the cleanup that way, but it's kind of broken that JNI_OnUnload() doesn't actually do what it is supposed to, and even more odd that the JNI documentation is completely silent as to why it doesn't actually work.
在 GNU Classpath (Kaffe 算是其「超集」) 中,package gnu.xml.libxmlj.util 用到這個技巧,可參閱 gnu/xml/libxmlj/util/XMLJ.java 在 initialization 的部份程式碼,其 JNI native impl 位於 native/jni/xmlj/xmlj_util.c。
由 jserv 發表於 February 19, 2006 04:01 PM