使用JNIEnv全局变量调用FindClass()等函数发生crash

本文探讨了在Android开发中因不当使用JNIEnv全局变量而导致应用崩溃的问题。详细分析了错误代码,并提供了有效的解决方案,包括使用JavaVM全局变量、AttachCurrentThread()及NewGlobalRef()等方法。

[Crash Log]

[html]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x98  
  2. Stack frame D/CrashAnrDetector(  859):     #00 pc 000b16f4  /system/lib/libart.s  
  3. o (art::JniAbort(char const*, char const*)+47)  
  4. Stack frame D/CrashAnrDetector(  859):     #01 pc 000b2045  /system/lib/libart.s  
  5. o (art::JniAbortF(char const*, char const*, ...)+60)  
  6. Stack frame D/CrashAnrDetector(  859):     #02 pc 000b4d9f  /system/lib/libart.s  
  7. o (art::ScopedCheck::ScopedCheck(_JNIEnv*, int, char const*)+334)  
  8. <span style="color:#FF0000;">Stack frame D/CrashAnrDetector(  859):     #03 pc 000b5e35  /system/lib/libart.s  
  9. o (art::CheckJNI::FindClass(_JNIEnv*, char const*)+20)</span>  
  10. <span style="color:#FF0000;">Stack frame D/CrashAnrDetector(  859):     #04 pc 0003f554  /data/app/com.nation  
  11. sky.rc.samsung-1/lib/arm/librc.so: Routine _JNIEnv::FindClass(char const*) at D:</span>  
  12. /dev/android-ndk-r10e/platforms/android-21/arch-arm/usr/include/jni.h:519  
  13. Stack frame D/CrashAnrDetector(  859):     #05 pc 0007c174  /data/app/com.nation  
  14. sky.rc.samsung-1/lib/arm/librc.so: Routine event_handler at E:\MDM\RemoteControl  
  15. l-sansang2\RemoteControll-sansang/jni/remotecontroll/remotecontroll.cpp:35625  
  16. Stack frame D/CrashAnrDetector(  859):     #06 pc 000662b8  /data/app/com.nation  
  17. sky.rc.samsung-1/lib/arm/librc.so: Routine call_user at E:\MDM\RemoteControll-sa  


[Crash原因分析]

出错的代码:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. jclass remotedesktopclass = sJniEnv->FindClass("com/xxx/xxx/xxx/xxx");  

sJniEnv是一个全局变量,但其实不应该全局保存JNIEnv。

这是一个和thread紧密关联的变量,在其他线程中使用就会crash。


【参考】

http://blog.youkuaiyun.com/freechao/article/details/7692239


【解决方式】

1.参考了第一篇文章,保存一个JavaVM全局变量,然后再其他线程中使用AttachCurrentThread(),代码如下:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, __null };  
  2. JNIEnv* jniEvn = __null;  
  3. int res = javaVm->AttachCurrentThread(&jniEvn, &args);  
  4.        jclass tmp = jniEvn->FindClass("com/xxx/xx/xx/xxxxx");  

2.但这么做仍然后crash,是说4.0上回收机制有改变,需要使用NewGlobalRef()生成全局变量才能用
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. remotedesktopclass = (jclass)jniEnv->NewGlobalRef(tmp);  

 

3.上面两部修改后其实还是不行,Log如下:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. Line 2149: F/art     ( 1223): sart/runtime/check_jni.cc:65] JNI DETECTED ERROR IN APPLICATION: <span style="color:#FF0000;">JNI NewGlobalRef called with pending exception 'java.lang.ClassNotFoundException' thrown in unknown throw location</span>  
  2. Line 2151: F/art     ( 1223): sart/runtime/check_jni.cc:65]     in call to NewGlobalRef  
  3. Line 2153: F/art     ( 1223): sart/runtime/check_jni.cc:65] "keyPress" prio=5 tid=14 Runnable  
  4. Line 2155: F/art     ( 1223): sart/runtime/check_jni.cc:65]   | group="main" sCount=0 dsCount=0 obj=0x12ccb080 self=0xb7c98350  
  5. Line 2157: F/art     ( 1223): sart/runtime/check_jni.cc:65]   | sysTid=1271 nice=0 cgrp=apps/bg_non_interactive sched=0/0 handle=0xb7c94b20  
  6. Line 2159: F/art     ( 1223): sart/runtime/check_jni.cc:65]   | state=R schedstat=( 1043961255 45210048 198 ) utm=94 stm=10 core=1 HZ=100  

最终将NewGlobalRef()移到主线程后解决此问题。



总结:

修改地方1:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. JNIEXPORT void JNICALL Java_xx_xx_xx_xx_xxx(JNIEnv * jniEnv, jobject object, jstring jflowNum, jstring judid){  
  2.      jniEnv->GetJavaVM(&javaVm);  
  3.      jclass tmp = jniEnv->FindClass("com/nationsky/rc/util/RemoteDesktopUtil");  
  4.      remotedesktopclass = (jclass)jniEnv->NewGlobalRef(tmp);  
  5.      .............  
修改地方2:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. static void keyPress(int keyCode){  
  2.     JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, __null };  
  3.     JNIEnv* jniEvn = __null;  
  4.     int res = javaVm->AttachCurrentThread(&jniEvn, &args);  
  5.         method = jniEvn->GetStaticMethodID(remotedesktopclass,  
  6.                                "pressHome""()V");  
  7.         .................  
<think>好的,用户给出了一段日志,里面多次出现SIGABRT信号导致的崩溃。我需要先分析SIGABRT的原因,然后根据可能的常见问题来推测问题所在,并提供解决方法。 首先,SIGABRT通常是程序主动调用了abort()函数引发的,常见于检测到严重错误,比如内存损坏、断言失败、堆栈溢出等。日志中多次不同进程的崩溃,可能意味着问题具有普遍性,而不是某个特定进程的问题。例如,可能代码中有共同的逻辑错误,或者资源竞争、多线程问题。 接下来,用户需要逐步排查。第一步是查看崩溃时的堆栈跟踪,比如使用adb logcat或Android Studio的调试工具,找到abort的具体位置。如果没有堆栈信息,可能需要配置NDK的addr2line或使用breakpad来符号化堆栈。 然后,考虑可能的原因:内存问题如越界访问、使用已释放内存;断言失败,比如检查条件不满足;资源泄露如文件描述符或内存泄露导致系统杀死进程;多线程问题如死锁或不正确同步;第三方库或系统API的误用;JNI层的错误,比如本地代码未处理异常。 解决方法方面,建议用户启用更详细的日志,使用AddressSanitizer或Valgrind检测内存错误,检查代码中的断言和错误处理逻辑,检查资源管理,分析多线程代码,以及审查JNI代码。此外,还可以尝试复现问题并逐步调试,或者查看系统日志中是否有相关错误信息。 需要确保回答结构清晰,分点列出可能原因和解决方法,帮助用户逐步排查问题。同时,注意使用中文,并正确应用LaTeX格式,但根据用户提供的日志内容,可能不需要数学公式,所以主要使用自然语言解释。</think>这段日志中的`SIGABRT`崩溃问题分析及解决方法如下: --- ### **一、问题本质** `SIGABRT`(Signal 6)是进程主动终止自身的信号,通常由以下原因触发: 1. **内存操作错误**:如堆栈溢出、双重释放(double-free)、非法指针访问 2. **断言失败**:代码中`assert()`检查到非法状态 3. **未捕获的C++异常**:如`std::bad_alloc`(内存分配失败) 4. **Android系统限制**:如文件描述符泄漏超过阈值(通过`adb shell lsof -p PID`可验证) 5. **JNI层错误**:本地代码(Native Code)未正确处理异常 --- ### **二、关键日志特征分析** ```log # 特征示例 pid-3360 A signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr -------- ``` - `fault addr --------`表示无明确内存地址,说明崩溃点可能在逻辑检查层而非直接的内存访问 - 多进程连续崩溃(pid-3360→pid-3652)暗示存在 **公共组件问题** 或 **资源竞争** --- ### **三、排查步骤** #### 1. 获取完整堆栈轨迹 通过以下命令捕获Native崩溃堆栈: ```bash adb logcat -b crash | grep "pid-3360" ``` 或使用Android Studio的`Debug`视图捕获符号化后的堆栈 #### 2. 常见问题验证 | 问题类型 | 验证方法 | |------------------|--------------------------------------------------------------------------| | 内存泄漏 | 使用`Android Profiler`观察内存增长曲线 | | JNI错误 | 检查`JNIEnv`调用是否在正确的线程,或通过`CheckJNI`模式运行(`adb shell setprop debug.checkjni 1`) | | 文件描述符泄漏 | `adb shell ls -l /proc/<PID>/fd` | | 堆栈溢出 | 检查递归函数或大体积局部变量(如`char buf[1024*1024]`) | #### 3. 代码审查重点 - **断言语句**:检查`assert(condition)`中`condition`的合法性 - **资源释放**:成对操作(如`malloc/free`、`open/close`)是否遗漏 - **多线程同步**:共享数据是否通过锁(`pthread_mutex`)保护 - **第三方库兼容性**:观察崩溃是否仅在特定API版本或设备上复现 --- ### **四、解决方法** #### 1. 内存问题定位 ```bash # 使用AddressSanitizer(ASan)编译调试版本 ndkBuildArgs += "-fsanitize=address -fno-omit-frame-pointer" ``` ASan会检测内存越界、释放后使用(use-after-free)等问题 #### 2. 捕获JNI崩溃 在JNI入口添加全局异常处理: ```cpp jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) return -1; // 注册全局异常处理器 if (sigsetjmp(env->GetLongField(...), 1)) { __android_log_print(ANDROID_LOG_ERROR, "JNI", "Native crash caught!"); return JNI_ERR; } return JNI_VERSION_1_6; } ``` #### 3. 监控资源泄漏 ```bash # 监控文件描述符数量 adb shell watch -n 1 "ls -l /proc/`pidof your.package`/fd | wc -l" ``` --- ### **五、典型修复案例** - **案例1**:某图片加载库因未关闭`Bitmap`导致FD泄漏 → 修复后FD数量稳定在50以下 - **案例2**:JNI中未检查`FindClass()`返回值 → 添加`if (clazz == NULL) return;`后崩溃消失 建议优先通过`adb bugreport`导出完整系统日志进行深度分析。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值