JNI getObjectClass使VM崩溃

我试图做一个事情,C ++通过JNI记录到Java端。

到目前为止,我有一个本地的制定者:

public native void setLogger(Logger logger); 

我用C ++实现的:

 void __stdcall Java_setLogger(JNIEnv* env, jobject, jobject loggerInstance) { logger = Logger(env, &loggerInstance); } 

Logger类的构造函数如下所示:

 Logger::Logger(JNIEnv* env, jobject* loggerInstance) { this->env = env; this->loggerInstance = *loggerInstance; } 

当我尝试通过提供的记录器实例(来自Java)上的JNI调用java方法时,它会使虚拟机崩溃。 我不知道为什么,因为这是从其他stackoverflow问题拼凑在一起。

 void Logger::debug(const std::string code, const std::string message) const { std::cout << "debug!" <GetObjectClass(loggerInstance); std::cout << "class retrieved" << std::endl; //for debugging purposes if (loggerClass == NULL) { std::cout << "logger class null" <GetMethodID(loggerClass, "debug", "(Ljava/lang/String;Ljava/lang/String;)V"); std::cout << "method retrieved" << std::endl; //for debugging purposes if (debugMethod == NULL) { std::cout << "debug method null" <CallVoidMethod(loggerInstance, debugMethod, code, message); } 

控制台输出:

 debug! # # A fatal error has been detected by the Java Runtime Environment: # # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0000000067dbef23, pid=6812, tid=0x0000000000003354 

Java端记录器的调试方法是:

 public final void debug(String code, String message) { ... } 

最后,生成的错误日志文件提到:

 Internal exceptions (2 events): Event: 0.041 Thread 0x0000000000d9e800 Exception  (0x0000000780987ca8) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u121\8372\hotspot\ Event: 0.041 Thread 0x0000000000d9e800 Exception  (0x0000000780987f90) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u121\8372\hotspot\src\share\vm\prims 

也许我应该提一下,Java方面在技术上不是Java; 但它是Kotlin。


更新(已解决):

所以,我做了下面的答案,但后来我注意到我甚至没有去“调试!” COUT。 我很困惑,但后来我意识到,在我的析构函数中,我总是调用env->deleteGlobalRef(..) ,并且我有一个必要的默认构造函数(它什么都不初始化)能够声明Logger logger; 并稍后用C ++进行初始化。

在删除global ref之前添加if(loggerInstance != NULL)就可以解决所有问题。 这是因为C ++(visual c ++)在分配新值时会调用析构函数,并且在放置Logger logger;时实际上会分配一个新实例Logger logger; 某处(从我的观察反正)。

垃圾收集器可以在Java端移动对象,所以如果你像这样保存你的记录器对象: this->loggerInstance = *loggerInstance ,它可能会在JNI调用之间移动,所以当你尝试在GetObjectClass使用它时,指针可能会晃来晃去。

存储指向Java对象的指针的安全方法是使用NewGlobalRef ,它在对象上创建一个引脚

 this->loggerInstance = env->NewGlobalRef(*loggerInstance); 

只要确保使用DeleteGlobalRef时再次删除它(所以应用5的规则)。


我也会指出你最后一次调用: env->CallVoidMethod(loggerInstance, debugMethod, code, message); 不管用。 没有从std::stringjava.lang.String自动编组。 你可以调用NewStringUTF来创建一个Java字符串,并通过它。

我认为你必须使用jstring而不是c ++字符串本身

 void Logger::debug(const std::string code, const std::string message) const { std::cout << "debug!" << std::endl; //for debugging purposes jclass loggerClass = env->GetObjectClass(loggerInstance); std::cout << "class retrieved" << std::endl; //for debugging purposes if (loggerClass == NULL) { std::cout << "logger class null" << std::endl; return; } jstring javMessage = env->NewStringUTF((const char* )message.c_str()); jstring javCode = env->NewStringUTF((const char* )code.c_str()); jmethodID debugMethod = env->GetMethodID(loggerClass, "debug", "(Ljava/lang/String;Ljava/lang/String;)V"); std::cout << "method retrieved" << std::endl; //for debugging purposes if (debugMethod == NULL) { std::cout << "debug method null" << std::endl; return; } env->CallVoidMethod(loggerInstance, debugMethod, javCode, javMessage); }