在JNI实现中创建静态全局变量是不是很糟糕?

时间:2016-05-02 03:24:58

标签: java c performance static java-native-interface

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

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

在进行实际计算之前,C库需要首先从共享内存加载辅助数据结构(并返回指向" big" struct)的指针,或者通过提供的路径从磁盘加载如果结构在共享内存中不可用。

辅助结构将用作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绑定中创建一个静态全局变量。

2 个答案:

答案 0 :(得分:4)

是的,你应该避免JNI中的全局静态。

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

在这些时候使用本机类构造函数/终结器并分配/释放内存资源会更聪明。这也允许使用不同的配置,具体取决于哪个ClassLoader加载了您的类 - 例如在EJB应用程序容器中。

  

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

糟糕的假设。 JVM在幕后做了很多事情。

答案 1 :(得分:2)

  

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

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

JNI代码中的共享静态全局不错 per-se ,但建议您将其设置为线程安全的。如果确实发生了线程安全问题,则可能导致JVM硬崩溃,并且可能难以诊断。

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

<强>更新

例如:

   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(...);
       }
   }