【发布时间】:2014-07-30 18:40:15
【问题描述】:
所以我在 Java 中有以下 JNIManager 类。如您所见,在这个类中,我定义了一个名为 setUpBackGround(); 的本地方法;
public class JNIManager{
public native void setUpBackground();
public void messageMe(byte[] byteArray);
}
然后我用本机 (C) 代码实现了另一个类。我们称这个类为背景类。这个类做一些后台工作,并调用 JNIManager 类的 messageMe() 方法,传递一个 byte[]。
class Background{
JNIEnv* mJNIEnv;
jbyteArray mArray;
jobject mJObject;
Background(JNIEnv * env, jobject jObject){
mArray = env->NewByteArray(1040);
mJNIEnv = env;
mJObject = jObject;
}
virtual ~Background(){
mJNIEnv->DeleteLocalRef(mArray); //is this necessary?
}
void someMethod(){
jclass manager = mJNIEnv->GetObjectClass(mJObject);
jmethodID method = mJNIEnv->GetMethodID(manager, "messageMe", "([B)V");
mJNIEnv->CallVoidMethod(mJObject, method, mArray);
mJNIEnv->DeleteLocalRef(manager); // is this necessary?
}
}
现在,在本机方法 setUpBackground 中,我执行以下操作,
JNIEXPORT jlong JNICALL Java_com_example_test_JNIManager_setUpBackground
(JNIEnv * env, jobject jo){
Background* back = new Background(env,jo);
return 0;
}
最后,在另一个类的方法中,我创建了一个 JNIManager 实例并调用本地方法 setUpBackground()。
otherMethod(){
JNIManager jniManager = new JNIManager();
jniManager.setUpBackground();
}
我对整个设置有 2 个问题。
1) 当 jniManager 在上述方法结束时超出范围时,我使用“new”关键字动态创建的 Background 类会自动被垃圾收集吗?我认为它不会并且会导致内存泄漏。它是否正确?如果是这样,我该怎么做才能纠正它?
2) DeleteLocalRef() 调用是否是避免内存泄漏所必需的,或者 JVM 是否会在它们不再使用时负责删除它们?
----------------------------------------------- --------------------------------------------
更新 - 遵循 nneonneo 的回答
public class JNIManager{
private long nativeHandle;
public JNIManager(){
nativeHandle = setUpBackground();
}
public native long setUpBackground();
public native void releaseBackground(long handle);
public void messageMe(byte[] byteArray) {//do some stuff};
}
更新背景类
class Background{
public:
JavaVM* mJvm;
JNIEnv* mJNIEnv;
jobject mJObject;
jbyteArray mArray;
int file;
Background(JNIEnv * env, jobject jObject){
env->GetJavaVM(&mJvm);
attachToThread();
mJObject = env->NewGlobalRef(jObject);
mArray = env->NewByteArray(1040);
file = 1; //Does this need to be a globalRef ?
}
void destroy(){
mJNIEnv->DeleteGlobalRef(mArray);
mJNIEnv->DeleteGlobalRef(mJObject);
mJvm = NULL;
mJNIEnv = NULL;
file = 0;
}
void someMethod(){
attachToThread();
jclass manager = mJNIEnv->GetObjectClass(mJObject);
jmethodID method = mJNIEnv->GetMethodID(manager, "messageMe", "([B)V");
mJNIEnv->CallVoidMethod(mJObject, method, mArray);
mJNIEnv->DeleteLocalRef(manager);
detachFromThread();
}
void attachToThread(){
mJvm->AttachCurrentThread(&mJNIEnv, NULL);
}
void detachFromThread(){
mJvm->DetachCurrentThread();
}
}
更新的原生方法
JNIEXPORT jlong JNICALL Java_com_example_test_JNIManager_setUpBackground
(JNIEnv * env, jobject jo){
Background* back = new Background(env,jo);
return reinterpret_cast<jlong>(back);
}
JNIEXPORT jlong JNICALL Java_com_example_test_JNIManager_releaseBackground
(JNIEnv * env, jobject jo, jlong handle){
Background* back = reinterpret_cast<Background* back>(handle);
back.destroy();
delete back;
}
更新了其他方法
otherMethod(){
JNIManager jniManager = new JNIManager();
//Do some stuff...
jniManager.releaseBackground();
}
我不确定第一点。
“JNIEnvs 在 JNI 调用之间可能不会保持不变(特别是,两个不同的线程会有不同的 JNIEnvs)”。
这是否意味着一旦 setupBackground() JNI 调用返回,当前线程就被删除了? 既然Background类是在这个线程中创建的,那么这个类的方法(比如someMethod())会不会运行在同一个线程上呢?如果不是,attachToThread() 方法是否定义了获取当前线程并使用其 JNIEnv* 的正确方法?
我基本上需要创建的 Background 对象在 JNIManager 的整个生命周期中都存在。然后偶尔调用背景对象的 someMethod() (通过一些外部类),然后这将调用 messageMe() 中的方法 JNIManager 类(可以在代码中看到)。
【问题讨论】:
-
谁以及如何打电话给
Background::someMethod()? -
它基本上是一个在我使用的背景类内部提供的侦听器方法。我只是扩展背景类并覆盖侦听器来做一些自定义的东西。因此,它由库代码调用..
标签: java c++ c memory-leaks java-native-interface