我是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绑定中创建一个静态全局变量。
答案 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(...);
}
}