JNI getObjectClass崩溃了VM

时间:2017-05-29 08:58:04

标签: java c++ java-native-interface

我正在努力做一个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方法时,它会崩溃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端的记录器上的调试方法是:

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;但它是科特林。

更新(已解决):

所以,我按照下面的答案说,但后来我注意到我甚至没有进入&#34;调试!&#34; COUT。我很困惑但后来我意识到在我的析构函数中,我总是调用env->deleteGlobalRef(..)并且我有一个必要的默认构造函数(没有初始化)以便能够声明Logger logger;并在以后用C ++初始化它

在删除全局之前简单地添加if(loggerInstance != NULL) ref解决了所有问题。这是因为当我分配一个新值时,C ++(visual c ++)调用析构函数,并且在将Logger logger;置于某处时(实际上从我的观察中)实际上它会分配一个新实例。

2 个答案:

答案 0 :(得分:2)

垃圾收集器可以在Java端移动对象,因此如果您保存您的logger对象:this->loggerInstance = *loggerInstance,它可能会在JNI调用之间移动,因此当您尝试在{{{ 1}}指针可能悬空。

存储指向Java对象的指针的一种安全方法是使用NewGlobalRef在对象上创建 pin

GetObjectClass

确保在使用this->loggerInstance = env->NewGlobalRef(*loggerInstance); 后再次删除它(因此请使用规则5)。

我还会指出您的上一次电话:DeleteGlobalRef无效。从env->CallVoidMethod(loggerInstance, debugMethod, code, message);std::string没有自动编组。您可以调用NewStringUTF来创建Java String,然后传递它。

答案 1 :(得分:0)

我认为你必须使用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);
}