You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JNI二次调用Java字符串方法触发SIGSEGV崩溃求助

问题描述

开发机器人应用时,通过ROS调用C++原生方法向Java传递字符串参数调用TTS功能,首次调用正常,第二次触发Fatal signal 11 (SIGSEGV)崩溃。日志显示Java端已打印传入文本,但执行toCharArray()时崩溃。

C++代码

void onSpeech(const std_msgs::String::ConstPtr &msg){
    log("RECEIVED SPEECH");
    log(msg->data.c_str());

    myVM->AttachCurrentThread(&myEnv, nullptr);
    jstring stringMsg = myEnv->NewStringUTF(msg->data.c_str());
    log("CALL JAVA METHOD");
    jmethodID mid = myEnv->GetStaticMethodID(myClass, "setSpeech", "(Ljava/lang/String;)V");
    myEnv->CallStaticVoidMethod(myClass, mid, stringMsg);

    log("START WAITING");
    ros::Time begin = ros::Time().now();
    while(begin + ros::Duration(2,0) > ros::Time().now()){ }
    log("WAIT FINISHED");

    log("CALL RELEASE");
    myEnv->ReleaseStringUTFChars(stringMsg, msg->data.c_str());
    myVM->DetachCurrentThread();
}

Java代码

public static void setSpeech(String text) {
    System.out.println("JAVA CALL");
    System.out.println("PASSED TEXT: " + text);
    char[] newChar = text.toCharArray();
    System.out.println("CHAR: " + Arrays.toString(newChar));
    String newString = String.copyValueOf(newChar);
    System.out.println("NEW STRING: " + newString);
    //Below is just the ttsmethod like this: startSpeak(newString)
}

运行日志

I/CPP: Hello 1
I/CPP: CALL JAVA METHOD
I/System.out: JAVA CALL
I/System.out: PASSED TEXT: Hello 1
I/System.out: CHAR: [H, e, l, l, o,  , 1]
I/System.out: NEW STRING: Hello 1
I/CPP: START WAITING
I/System.out: SPEECH STARTED: Hello 1
I/System.out: SPEECH FINISHED
I/CPP: WAIT FINISHED
I/CPP: CALL RELEASE
//Now I call the method again
I/CPP: RECEIVED SPEECH
I/CPP: Hello 2
I/CPP: CALL JAVA METHOD
I/System.out: JAVA CALL
I/System.out: PASSED TEXT: Hello 2
A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x38206f in tid 8890 (Thread-117)

补充logcat日志

2022-12-19 13:44:28.785 9455-9499/rob.rosloomo I/ROSCPP_NDK: RECEIVED SPEECH
2022-12-19 13:44:28.786 9455-9499/rob.rosloomo I/ROSCPP_NDK: Hello 9
2022-12-19 13:44:28.787 9455-9499/rob.rosloomo I/ROSCPP_NDK: CALL JAVA METHOD
2022-12-19 13:44:28.788 9455-9499/rob.rosloomo I/System.out: JAVA CALL
2022-12-19 13:44:28.788 9455-9499/rob.rosloomo I/System.out: PASSED TEXT: Hello 9
2022-12-19 13:44:28.788 9455-9499/rob.rosloomo A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x39206f in tid 9499 (Thread-130)
2022-12-19 13:44:28.893 2750-2750/? I/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
2022-12-19 13:44:28.893 2750-2750/? I/DEBUG: Build fingerprint: 'i-Buddie/VA50EC_i_Buddie/VA50EC_1:5.1.1/LMY47Z/78:user/test-keys'
2022-12-19 13:44:28.893 2750-2750/? I/DEBUG: Revision: '0'
2022-12-19 13:44:28.893 2750-2750/? I/DEBUG: ABI: 'x86'
2022-12-19 13:44:28.894 2750-2750/? I/DEBUG: pid: 9455, tid: 9499, name: Thread-130  >>> rob.rosloomo <<<
2022-12-19 13:44:28.894 2750-2750/? I/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x39206f
2022-12-19 13:44:28.920 2750-2750/? I/DEBUG:     eax f3cd0040  ebx f3bfca2c  ecx f3cd0000  edx 00000001
2022-12-19 13:44:28.921 2750-2750/? I/DEBUG:     esi 0039206f  edi f3ef006c
2022-12-19 13:44:28.921 2750-2750/? I/DEBUG:     xcs 00000023  xds 0000002b  xes 0000002b  xfs 00000097  xss 0000002b
2022-12-19 13:44:28.921 2750-2750/? I/DEBUG:     eip f38b4e12  ebp e2a23d78  esp e2a23d40  flags 00210206
2022-12-19 13:44:28.921 2750-2750/? I/DEBUG: backtrace:
2022-12-19 13:44:28.921 2750-2750/? I/DEBUG:     #00 pc 001a5e12  /system/lib/libart.so (art::gc::allocator::RosAlloc::RefillRun(art::Thread*, unsigned int)+290)
2022-12-19 13:44:28.921 2750-2750/? I/DEBUG:     #01 pc 001a60fa  /system/lib/libart.so (art::gc::allocator::RosAlloc::AllocFromRun(art::Thread*, unsigned int, unsigned int*, unsigned int*, unsigned int*)+666)
2022-12-19 13:44:28.921 2750-2750/? I/DEBUG:     #02 pc 00155f8d  /system/lib/libart.so (art::mirror::Object* art::gc::space::RosAllocSpace::AllocCommon<true>(art::Thread*, unsigned int, unsigned int*, unsigned int*, unsigned int*)+109)
2022-12-19 13:44:28.921 2750-2750/? I/DEBUG:     #03 pc 0040571d  /system/lib/libart.so (_ZN3art6mirror5Array5AllocILb0EEEPS1_PNS_6ThreadEPNS0_5ClassEijNS_2gc13AllocatorTypeEb.constprop.104+989)
2022-12-19 13:44:28.921 2750-2750/? I/DEBUG:     #04 pc 00406a72  /system/lib/libart.so (artAllocArrayFromCodeResolvedRosAlloc+162)
2022-12-19 13:44:28.921 2750-2750/? I/DEBUG:     #05 pc 000e5815  /system/lib/libart.so (art_quick_alloc_array_resolved_rosalloc+37)
2022-12-19 13:44:28.921 2750-2750/? I/DEBUG:     #06 pc 00079bfb  /data/dalvik-cache/x86/system@framework@boot.oat
2022-12-19 13:44:29.120 2750-2750/? I/DEBUG: Tombstone written to: /data/tombstones/tombstone_06
2022-12-19 13:44:29.120 3124-9562/? W/ActivityManager:   Force finishing activity 1 rob.rosloomo/.MainActivity
2022-12-19 13:44:29.129 3124-3255/? E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!
2022-12-19 13:44:29.131 3124-9562/? E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!
2022-12-19 13:44:29.131 3124-9562/? W/ActivityManager: Exception thrown during pause
问题分析与修复

核心问题

  1. 错误使用ReleaseStringUTFChars:NewStringUTF创建的jstring内部会复制传入的UTF-8字符串,不需要调用该方法释放。此方法仅适用于GetStringUTFChars获取的字符指针,直接调用会破坏JVM内存管理逻辑,首次调用未崩溃属于侥幸,第二次触发内存异常。
  2. 线程附着逻辑不安全:每次调用都执行AttachCurrentThread,未检查线程是否已附着,重复附着会导致线程状态异常。
  3. 硬等待不合理:空循环等待2秒占用CPU,且无法确保Java端TTS操作真正完成。

修复后的C++代码

void onSpeech(const std_msgs::String::ConstPtr &msg){
    log("RECEIVED SPEECH");
    log(msg->data.c_str());

    // 检查线程是否已附着到JVM
    jint result = myVM->GetEnv((void**)&myEnv, JNI_VERSION_1_6);
    if (result == JNI_EDETACHED) {
        if (myVM->AttachCurrentThread(&myEnv, nullptr) != JNI_OK) {
            log("Failed to attach thread");
            return;
        }
    }

    jstring stringMsg = myEnv->NewStringUTF(msg->data.c_str());
    log("CALL JAVA METHOD");
    jmethodID mid = myEnv->GetStaticMethodID(myClass, "setSpeech", "(Ljava/lang/String;)V");
    if (mid != nullptr) {
        myEnv->CallStaticVoidMethod(myClass, mid, stringMsg);
    } else {
        log("Failed to get method ID");
    }

    // 释放本地引用的jstring对象
    myEnv->DeleteLocalRef(stringMsg);

    // 仅当本次调用附着的线程才执行分离
    if (result == JNI_EDETACHED) {
        myVM->DetachCurrentThread();
    }
}

优化建议

  1. Java端回调同步:替换硬等待,通过回调通知C++端TTS完成:
public static void setSpeech(String text, Runnable onComplete) {
    System.out.println("JAVA CALL");
    System.out.println("PASSED TEXT: " + text);
    // 执行TTS操作并在完成时触发回调
    startSpeak(text, new TTSListener() {
        @Override
        public void onFinish() {
            onComplete.run();
        }
    });
}
  1. 缓存方法ID:GetStaticMethodID耗时,建议在JNI初始化时缓存mid,避免每次调用重复查询。
  2. 增强错误处理:添加JNI调用的异常检查(如ExceptionCheck),及时捕获Java端异常并处理。

内容的提问来源于stack exchange,提问作者Tamoo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 14:18:09