JNI - 有没有办法让 JVM 调用 JNI_OnUnload ?

hae*_*lix 6 c++ java java-native-interface shared-libraries jnienv

关于这方面的信息很少,例如2006 年的这个人们放弃了优雅地释放 JNI 资源。

JNI_OnUnload( Oracle 文档) 旨在当 JVM 中不再需要您的本机功能时释放资源(特别是“全局引用”)。本机库将像这样加载

static {
    System.loadLibrary("mylibjni"); // on Linux this translates to "libmylibjni.so"
}
Run Code Online (Sandbox Code Playgroud)

...但是 Java 没有提供明确的卸载方法。

发生了什么:在我的 lib 中,由于没有机会进行清理,lib 最终确实卸载了(我不明白什么时候,请参阅下面的调用堆栈),但是为时已晚,这会触发一些拥有 JNI 全局引用的全局变量调用DeleteGlobalRef失败:

# JRE version: OpenJDK Runtime Environment (10.0.2+13) (build 10.0.2+13)
# Java VM: OpenJDK 64-Bit Server VM (10.0.2+13, mixed mode, tiered, compressed oops, g1 gc, linux-amd64)
# Problematic frame:
# C  [libblahjni.so+0x9a7e6]  JNIEnv_::DeleteGlobalRef(_jobject*)+0x14
Run Code Online (Sandbox Code Playgroud)

深入研究一下,这是因为线程上没有JNIEnv执行卸载(我正在缓存环境thread_local)。当时尝试检索 Env 没有成功 - 我尝试使用JavaVM::GetEnvand JavaVM::AttachCurrentThread,似乎即使附加工作正常,JavaVM::GetEnv仍然返回JNI_EDETACHED (-2)。所以想必,在 JVM 生命周期中再附加/拥有环境已经太晚了。

我尝试过其他一些事情 - 根据上面 2006 年的文章,我尝试用 Java 做System.runFinalizersOnExit(true),但没有运气。尽管我确实看到终结器被调用(与不调用 runFinalizers 时相反,请参见下面的输出),但JNI_OnUnload仍然没有被调用。

Checked JNI functions are being used to validate JNI usage
[Dynamic-linking native method java.lang.Shutdown.runAllFinalizers ... JNI]
Checked JNI functions are being used to validate JNI usage
[Dynamic-linking native method java.lang.Shutdown.halt0 ... JNI]
Run Code Online (Sandbox Code Playgroud)

作为最后一条信息,lib 卸载调用堆栈如下所示。我不明白它从哪里来。我使用的是最新的 Arch linux、Java 10,如果重要的话,我正在 Docker 中运行。我想原则上解决优雅退出问题,因为这可以让您相信您可以轻松地发现项目生命周期后期发生的问题。

[Dynamic-linking native method java.lang.Shutdown.runAllFinalizers ... JNI]
Checked JNI functions are being used to validate JNI usage
[Dynamic-linking native method java.lang.Shutdown.halt0 ... JNI]
 0# (some more code of my own ommitted at the top of the stack)
 1# std::_Optional_payload<...
 2# std::_Optional_base<...
 3# std::optional<...>::~optional() in /orion/mpf-jni/Debug/libmylibjni.so
 4# 0x00007FDCEC5E845C in /usr/lib/libc.so.6
 5# 0x00007FDCEC5E858E in /usr/lib/libc.so.6
 6# 0x00007FDCEB919B89 in /usr/lib/jvm/java-10-openjdk/lib/server/libjvm.so
 7# 0x00007FDCEBDF58C6 in /usr/lib/jvm/java-10-openjdk/lib/server/libjvm.so
 8# 0x00007FDCEBDF5346 in /usr/lib/jvm/java-10-openjdk/lib/server/libjvm.so
 9# 0x00007FDCEBDF2770 in /usr/lib/jvm/java-10-openjdk/lib/server/libjvm.so
10# 0x00007FDCEBDF3D2F in /usr/lib/jvm/java-10-openjdk/lib/server/libjvm.so
11# 0x00007FDCEBDF3FBE in /usr/lib/jvm/java-10-openjdk/lib/server/libjvm.so
12# 0x00007FDCEBC0558A in /usr/lib/jvm/java-10-openjdk/lib/server/libjvm.so
13# 0x00007FDCEC378A9D in /usr/lib/libpthread.so.0
14# clone in /usr/lib/libc.so.6
  1447612069173.971  Fatal| could not get JVM env object for JNI version [65540], error [-2]
Run Code Online (Sandbox Code Playgroud)