不要忘记JNI的异常处理

2 篇文章 0 订阅
1 篇文章 0 订阅

近日,Android App 出现了异常退出。Log上能看到的信息类似:

attempt to use stale local reference 0x1
VM aborting
Fatal signal 6 (SIGABRT) at 0x00007efc

google后,大部分的连接都指向了local refrence问题。比如

http://android-developers.blogspot.tw/2011/11/jni-local-reference-changes-in-ics.html

http://stackoverflow.com/questions/36227632/what-is-jni-error-app-bug-attempt-to-use-stale-local-reference-0x1-how-to

http://stackoverflow.com/questions/9978011/android-ics-jni-error-attempt-to-use-stale-local-reference-0x1

http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design.html#wp16785

这些主要说的是,JNI API返回的对象引用都是local reference的(Oracle的Doc上有明确保证),在本次Native API调用结束后就会失效(理论上不能再使用了)。如果要持久化供后续访问,就需要生成global的引用。然而经过仔细检查,尽管出错日志非常类似,但是代码可以排除local/global引用的问题。


经过很多次的假设和排除,最后发现问题出现在异常上。

在调用JNI API时,代码已经对返回值的正确性进行了判断,比如是否空指针,但是这还远远不够。根据JNI的文档:

The JNI allows native methods to raise arbitrary Java exceptions. The native code may also handle outstanding Java exceptions. The Java exceptions left unhandled are propagated back to the VM.

Certain JNI functions use the Java exception mechanism to report error conditions. In most cases, JNI functions report error conditions by returning an error code and throwing a Java exception. The error code is usually a special return value (such as NULL) that is outside of the range of normal return values. Therefore, the programmer can:

  • quickly check the return value of the last JNI call to determine if an error has occurred, and
  • call a function, ExceptionOccurred(), to obtain the exception object that contains a more detailed description of the error condition.

There are two cases where the programmer needs to check for exceptions without being able to first check an error code:

  • The JNI functions that invoke a Java method return the result of the Java method. The programmer must call ExceptionOccurred()to check for possible exceptions that occurred during the execution of the Java method
  • Some of the JNI array access functions do not return an error code, but may throw an ArrayIndexOutOfBoundsException or ArrayStoreException
In all other cases, a non-error return value guarantees that no exceptions have been thrown.


按照文档的描述,除返回NULL外,JNI还会使用Java的异常处理机制来抛出异常。native代码要处理这些异常,否则这些异常会传递回虚拟机。

Java的异常和native 代码的异常或者错误处理是不同的机制。实践中发现,并不是JNI调用发生异常时,就会在调用函数处进行栈展开,而是在后续的某个JNI调用时,才发生异常退出,比如开头提到的attempt to use stale local reference错误,就是因为某个JNI调用抛出了异常,没有进行处理;结果在后续第N个调用时,才提示错误并推出,logcat也没有打印java的异常信息。这导致问题比较难定位。

总结,以FindClass为例,正确的伪代码类似:

jclass clazz = env->FindClas(...)
if (clazz == NULL)
{
	if ((jthrowable ex = env->ExceptionOccurred()) != NULL)
	{
		// 打印异常信息等
		// .... dosth......


		// 必须清除异常
		env->ExceptionClear()
	}
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值