在 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 在幕后做了很多事情。
我是 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 在幕后做了很多事情。