JNI Linux分段故障

时间:2019-04-19 21:35:07

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

我的JNI库在Windows上可以完美运行,但是在Linux上,我总是遇到奇怪的分段错误。

siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x0000000000000000

崩溃文件中的堆栈尾部是这样的:

C  [libfmodjavaL.so+0xfb8c]  JNIEnv_::GetStaticObjectField(_jclass*, _jfieldID*)+0x18
C  [libfmodjavaL.so+0xf72b]  Logger::sendToSystemOut(bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)+0x75
C  [libfmodjavaL.so+0xf7c2]  Logger::log(char const*)+0x4c
C  [libfmodjavaL.so+0xd70d]  fmodDebugCallback(unsigned int, char const*, int, char const*, char const*)+0x127

因此,在调用Logger类中的GetStaticObject字段时,它似乎崩溃了。这就是这种方法:

void Logger::sendToSystemOut(bool error, std::string message) {
    JNIEnv* jni = FMODWrapper::utils->getJNI();

    jobject printStream;
    if (error) {
        printStream = jni->GetStaticObjectField(this->systemClass, this->errFieldID);
    } else {
        printStream = jni->GetStaticObjectField(this->systemClass, this->outFieldID);
    }

    jobject messageString = jni->NewStringUTF(message.c_str());
    jni->CallObjectMethod(printStream, this->printlnMethodID, messageString);
}

因此,我猜测有关存储这些字段的类和字段ID的事情是不正确的。但是很奇怪的是,我的库启动时,即使从FMOD调用fmodDebugCallback,我也会得到日志输出。

Logger::Logger(const char* name) {
    this->name = name;

    JNIEnv* jni = FMODWrapper::utils->getJNI();

    this->systemClass = FMODWrapper::utils->findClass("java/lang/System");
    this->outFieldID = jni->GetStaticFieldID(this->systemClass, "out", "Ljava/io/PrintStream;");
    this->errFieldID = jni->GetStaticFieldID(this->systemClass, "err", "Ljava/io/PrintStream;");

    jclass printStreamClass = FMODWrapper::utils->findClass("java/io/PrintStream");
    this->printlnMethodID = jni->GetMethodID(printStreamClass, "println", "(Ljava/lang/String;)V");
}

因此,日志记录在Windows上可以完美地工作,但是一段时间后在Linux上崩溃。在Fedora 29 64位上与g ++一起编译。

更新:我获取JNIEnv *的方法

JNIEnv* Utils::getJNI() {
    JNIEnv* jni;

    int getEnvResult = FMODWrapper::jvm->GetEnv((void**) &jni, JNI_VERSION_1_6);

    if (getEnvResult == JNI_EDETACHED) {
        FMODWrapper::jvm->AttachCurrentThread(ANDROID_VOIDPP_CAST &jni, nullptr);
    }

    return jni;
}

更新2:由于我收到日志消息,因此代码本身可以工作到一定程度。可能与线程有关? https://hastebin.com/kuzefuwawu.txt

2 个答案:

答案 0 :(得分:1)

systemClass,errFieldId和outFieldID都是从不同的JNIEnv获得的。

无法缓存JNIEnv: Keeping a global reference to the JNIEnv environment

就像无法缓存一样,您不能存储从其他JNIEnv获得的ID,这些ID您将不再使用,也不应使用来自该ID的任何东西。您需要从当前有效的JNIEnv中获取所有这些信息。

答案 1 :(得分:0)

问题不在于类引用或字段ID的线程亲和力。问题是使用超出其范围的本地类引用。这是某些JVM的实现细节,本地引用实际上不会过期。

解决方法是使用

Logger::Logger(const char* name) {
    this->name = name;
    JNIEnv* jni = FMODWrapper::utils->getJNI();
    this->systemClass = jni->NewGlobalRef(jni->findClass("java/lang/System"));
    …