在 JNI 实现中创建静态全局变量不好吗?

Is it bad to create static global variable in JNI implementations?

我是 JNI 的新手,所以这个天真的问题的答案可能是肯定的。

手头的问题是我正在实现 JNI 绑定到某些 C 库以完成繁重的工作。

在进行实际计算之前,C 库需要先从共享内存加载一个辅助数据结构(和 returns 指向 "big" 结构的指针),或者通过以下方式从磁盘加载如果结构在共享内存中不可用,则提供的路径。

辅助结构将用作const

为了避免每次都从磁盘加载,我正在考虑创建一个 static int 来表示大结构已正确初始化:

static int big_struct_loaded_in_mem = 0;

void Java_className_dowork(/*parameters*/){
    if(!big_struct_loaded_in_mem){
        // load struct
        big_struct_loaded_in_mem = 1;
    }
    // load from shared mem then do work using big_struct
    load_from_shm();
}

void Java_className_cleanup(/*parameters*/){
    //free up mem
    big_struct_loaded_in_mem = 0;
}

为简单起见,假设 Java 调用者和本机函数是单线程的。

非常感谢!

注意:当然,没有 static int 的简单修复可能只是每次调用 load_from_shm() 并测试返回的指针,但我是好奇这个想法是否不好:即在 JNI 绑定中创建一个静态全局变量。

To make things simple, assume that the Java caller and the native function are single-threaded.

这就是问题所在。如果事实证明您需要 从多个线程使用本机方法怎么办?如果它发生在你没有意识到的情况下怎么办?

JNI 代码中的共享静态全局变量 本身 不错,但建议您使其成为线程安全的。如果确实发生了线程安全问题,很容易导致JVM硬崩溃,诊断难度大。

还建议进行一些 Java 端检查以管理数据结构的生命周期。例如,您可能想做一些事情,以便在 "cleanup" 方法之后调用 "doWork" 方法时抛出 Java 异常。或者使数据结构的加载成为一个单独的方法调用。

更新

例如:

   public class BigStruct {
       static {
           // load native library
       }

       private static boolean loaded = false;
       private static boolean unloaded = false;

       private native boolean doLoad();
       private native boolean doUnload();
       private native void doComputation();

       public static synchronized void load() {
           if (loaded) {
               throw LoaderException("already loaded");
           }
           if (!doLoad()) {
               throw LoaderException("load failed");
           }
           loaded = true;
       }

       public static synchronized void unload() {
           if (unloaded) {
               throw LoaderException("already unloaded");
           }
           if (!doUnload()) {
               throw LoaderException("unload failed");
           }
           unloaded = true;
       }

       public static synchronized void computation(...) {
           if (!(loaded && !unloaded)) {
               throw LoaderException("wrong state");
           }
           doComputation(...);
       }
   }

是的,您应该避免在 JNI 中使用全局静态。

Java 可能加载 class 的多个实例(因为例如有多个 classloader)并且静态将在所有实例之间共享。不幸的是,清理调用也是如此,如果您的 class 的一个实例被销毁而另一个实例仍在使用中,则会导致不一致。

那时使用本机 class 构造函数/终结器和 allocate/deallocate 内存资源会更聪明。这也允许使用不同的配置等用例,具体取决于哪个 ClassLoader 加载了您的 class —— 例如在 EJB 应用程序容器中。

To make things simple, assume that the Java caller and the native function are single-threaded.

错误的假设。 JVM 在幕后做了很多事情。