【问题标题】:Is it bad to create static global variable in JNI implementations?在 JNI 实现中创建静态全局变量是不是很糟糕?
【发布时间】:2016-05-02 03:24:57
【问题描述】:

我是 JNI 的新手,所以这个幼稚问题的答案可能很简单。

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

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

    【讨论】:

    • 谢谢保罗。从您的回答中真正学到了新东西(ClassLoader)。
    【解决方案2】:

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

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

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

    还建议进行一些 Java 端检查以管理数据结构的生命周期。例如,您可能想要做一些事情,以便在“清理”方法之后调用“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(...);
           }
       }
    

    【讨论】:

    • 谢谢斯蒂芬。真的很欣赏未来派的观点!但我不确定的一件事是如何检查 Java 端 C 中加载的数据结构?我对 JNI 的理解是 JVM 向本机库开放了它的直觉,让 C 代码获取它可以/需要的东西,而不是相反。因此,如果 C 端出现问题,Java 端将无能为力。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-08
    • 2019-09-25
    • 1970-01-01
    • 2011-09-20
    • 2018-01-20
    • 2020-11-19
    相关资源
    最近更新 更多