我正在尝试让 C++ 通过 JNI 登录到 Java 端。
到目前为止,我有一个本地 setter:
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;
}
当我尝试通过 JNI 在提供的记录器实例(来自 Java)上调用 java 方法时,它使 VM 崩溃。我不确定为什么,因为这是从其他 stackoverflow 问题拼凑而成的。
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;
}
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, 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端logger的debug方法是:
public final void debug(String code, String message) {
...
}
最后,生成的错误日志文件提到:
Internal exceptions (2 events):
Event: 0.041 Thread 0x0000000000d9e800 Exception <a 'java/lang/NoSuchMethodError': Method sun.misc.Unsafe.defineClass(Ljava/lang/String;[BII)Ljava/lang/Class; name or signature does not match> (0x0000000780987ca8) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u121\8372\hotspot\
Event: 0.041 Thread 0x0000000000d9e800 Exception <a 'java/lang/NoSuchMethodError': Method sun.misc.Unsafe.prefetchRead(Ljava/lang/Object;J)V name or signature does not match> (0x0000000780987f90) thrown at [C:\re\workspace\8-2-build-windows-amd64-cygwin\jdk8u121\8372\hotspot\src\share\vm\prims
也许我应该提到 Java 端在技术上不是 Java;但它是 Kotlin。
更新(已解决):
所以,我按照下面的回答做了,但后来我发现我什至没有进入“调试!”算了。我很困惑,但后来我意识到在我的析构函数中,我总是调用 env->deleteGlobalRef(..)
并且我有一个必要的默认构造函数(不初始化任何东西)来声明 Logger logger;
并稍后在 C++ 中对其进行初始化。
在删除全局引用之前简单地添加 if(loggerInstance != NULL)
就解决了所有问题。这是因为 C++ (visual c++) 在我分配一个新值时调用析构函数,并且它实际上在将 Logger logger;
放在某个地方时分配了一个新实例(无论如何根据我的观察)。
最佳答案
垃圾收集器可以在 Java 端移动对象,所以如果你像这样保存你的记录器对象:this->loggerInstance = *loggerInstance
,它可能会在 JNI 调用之间移动,所以当您尝试在 GetObjectClass
中使用它时,指针可能会悬空。
存储指向 Java 对象的指针的安全方法是使用 NewGlobalRef
在对象上创建一个 pin:
this->loggerInstance = env->NewGlobalRef(*loggerInstance);
只需确保在使用完后使用 DeleteGlobalRef
再次删除它(因此应用规则 5)。
我还要指出,您最后一次调用:env->CallVoidMethod(loggerInstance, debugMethod, code, message);
将不起作用。没有从 std::string
到 java.lang.String
的自动编码。您可以调用NewStringUTF
创建一个 Java 字符串,并传递它。
关于java - JNI getObjectClass 使 VM 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44238438/