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
问题分析与修复
核心问题
- 错误使用
ReleaseStringUTFChars:NewStringUTF创建的jstring内部会复制传入的UTF-8字符串,不需要调用该方法释放。此方法仅适用于GetStringUTFChars获取的字符指针,直接调用会破坏JVM内存管理逻辑,首次调用未崩溃属于侥幸,第二次触发内存异常。 - 线程附着逻辑不安全:每次调用都执行
AttachCurrentThread,未检查线程是否已附着,重复附着会导致线程状态异常。 - 硬等待不合理:空循环等待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(); } }
优化建议
- 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(); } }); }
- 缓存方法ID:
GetStaticMethodID耗时,建议在JNI初始化时缓存mid,避免每次调用重复查询。 - 增强错误处理:添加JNI调用的异常检查(如
ExceptionCheck),及时捕获Java端异常并处理。
内容的提问来源于stack exchange,提问作者Tamoo
相关产品推荐
相关产品推荐

