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::string
到java.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); }