java - JNI getObjectClass 使 VM 崩溃

标签 java c++ java-native-interface

我正在尝试让 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::stringjava.lang.String 的自动编码。您可以调用NewStringUTF创建一个 Java 字符串,并传递它。

关于java - JNI getObjectClass 使 VM 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44238438/

相关文章:

Java 操作系统问题

c++ - 多线程写入套接字

c++ - 窗口事件回调函数 C++ 的问题

java - 将 C++ 对象实例存储在 JNI jobject 中并稍后检索

apache-spark - 如何将自定义库部署到 Apache Spark?

java - 继承链与多重扩展

java - Spring MVC POST 不支持的媒体类型

c++ - 如何动态转换模板类

java - 64 位编译器中的 JNI 未解析的外部符号 __imp_JNI_CreateJavaVM

java - 下载后留言