我有以下问题:
在我的java程序中我正在调用一个本机函数,它在C ++中创建一个“MyEventReceiver”类的对象,后来在那个java程序中我调用了一个调用该对象的“test”方法的本机函数。在这个“测试”方法中,我想调用一个java对象的方法,我用它来调用第二个本机函数,但是只要进行这个调用,jvm就会崩溃。
这是创建MyEventReceiver对象的本机代码:
JNIEXPORT jlong JNICALL Java_irr4jEventReceiver_native_1createEventReceiver
(JNIEnv *env, jobject obj){
MyEventReceiver *rec = new MyEventReceiver(env, obj);
return (long)rec;
}
这是我稍后在程序中使用的本机代码,用于调用该对象中的“test”方法:
JNIEXPORT void JNICALL Java_irr4jEventReceiver_native_1testmethode
(JNIEnv *env, jobject obj, jlong ptrrec){
MyEventReceiver *rec = (MyEventReceiver*)ptrrec;
rec->test();
}
这是MyEventReceiver
类:
class MyEventReceiver : public IEventReceiver
{
public:
JNIEnv *myenv;
jobject receiverobj;
jclass SEventclass;
jobject eventobj;
jmethodID cid;
jclass cevrec;
jmethodID meth2;
public:
void test(){
eventobj = myenv->AllocObject(SEventclass);
eventobj = myenv->NewObject(SEventclass, cid);
myenv->CallVoidMethod(receiverobj,meth2,eventobj); //this is the line that causes the crash
}
MyEventReceiver(JNIEnv *env, jobject obj)
{
this->myenv=env;
receiverobj = env->NewGlobalRef(obj);
SEventclass = myenv->FindClass("SEvent");
cid = myenv->GetMethodID(SEventclass,"<init>", "()V");
cevrec = myenv->FindClass("MyEventReceiver");
meth2 =myenv->GetMethodID(cevrec, "OnEvent", "(LSEvent;)V");
//test();
}
};
如果我在构造函数的末尾调用方法“test”,它就可以工作......只有我稍后在java程序之外调用它...我认为它与jobject“receiverobj有关“......似乎在一段时间后变得无效,但我不知道为什么 ...我删除了一些代码...删除了一些调试代码。我正在创建的“eventobj”很好......我可以调用该对象的其他方法,methodID也很好,只是行:
myenv->CallVoidMethod(receiverobj,meth2,eventobj);
给了我问题,我不知道为什么:)
崩溃消息是:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x67d594f4, pid=5292, tid=2400
#
# JRE version: 7.0_21-b11
# Java VM: Java HotSpot(TM) Client VM (23.21-b01 mixed mode, sharing windows-x86 )
# Problematic frame:
# V [jvm.dll+0xa94f4]
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# G:\irr4jjava\irr4j\hs_err_pid5292.log
#
# If you would like to submit a bug report, please visit:
# http://bugreport.sun.com/bugreport/crash.jsp
#
修改
感谢您的帮助。我试图使用全局引用和附加线程,但我不能让它工作。我不确定我是否理解附加线程的内容......
我修改了第一个本机方法来创建对jobject的全局引用并将其传递给构造函数: JavaVM * jvm;
JNIEXPORT jlong JNICALL Java_irr4jEventReceiver_native_1createEventReceiver
(JNIEnv *env, jobject obj){
jobject recobj = env->NewGlobalRef(obj);
env->GetJavaVM(&jvm);
MyEventReceiver *rec = new MyEventReceiver(recobj);
return (long)rec;
}
在“MyReceiverObject”类中,我尝试附加线程,但它仍然崩溃:
class MyEventReceiver : public IEventReceiver
{
public:
JNIEnv *myenv;
jobject receiverobj;
jclass SEventclass;
jobject eventobj;
jmethodID cid;
jclass cevrec;
jmethodID meth2;
public:
void test(){
jvm->AttachCurrentThread((void**)&myenv,NULL);
eventobj = myenv->AllocObject(SEventclass); //crash
eventobj = myenv->NewObject(SEventclass, cid);
myenv->CallVoidMethod(receiverobj,meth2,eventobj);
}
MyEventReceiver(jobject obj)
{
jvm->AttachCurrentThread((void**)&myenv,NULL);
receiverobj=obj;
SEventclass = myenv->FindClass("SEvent");
cid = myenv->GetMethodID(SEventclass,"<init>", "()V");
cevrec = myenv->FindClass("MyEventReceiver");
meth2 =myenv->GetMethodID(cevrec, "OnEvent", "(LSEvent;)V");
//test();
}
};
答案 0 :(得分:2)
您需要阅读JNI规范中有关全局参考的章节。您在JNI方法中传递的作业仅在该方法的生命周期内有效。在将它存储在比当前调用更长的任何地方之前,你需要将它转换为GlobalRef,也许是WeakGlobalRef。
类似的东西适用于JNIEnv指针。它仅在传递给它的方法的持续时间内有效。如果你想在不同的线程中使用一个,或者稍后你必须通过AttachThread()获得一个新的。