应用报未知错误
应用安装好连接电脑就能看错误信息
一般会是so库问题
怎么解决:so升级,so打包时间过老,不适合现在的种种环境,上网上搜索。
JNI ERROR (app bug): local reference table overflow (max=512)
看错误信息,应该本地指针块最大只要512个,当平凡调用之后,可能越界了,超出了范围,导致异常。
当线程从 Java 环境切换到 native code 上下文时(J2N),JVM 会分配一块内存,创建一个 Local Reference 表,这个表用来存放本次 native method 执行中创建的所有的 Local Reference。每当在 native code 中引用到一个 Java 对象时,JVM 就会在这个表中创建一个 Local Reference。比如我们调用 NewStringUTF() 在 Java Heap 中创建一个 String 对象后,在 Local Reference 表中就会相应新增一个 Local Reference。运行 nativemethod 的线程的堆栈记录着 Local Reference 表的内存位置,Local Reference 表中存放 JNI Local Reference,实现 Local Reference 到 Java 对象的映射。native method 代码间接访问 Java 对象。通过线程堆栈中的记录着 Local Reference 表的内存位置的指针定位相应的 Local Reference 的位置,然后通过相应的 Local Reference 映射到 Java 对象。
当 nativemethod 引用一个 Java 对象时,会在 Local Reference 表中创建一个新 Local Reference。在 Local Reference 结构中写入内容,实现 Local Reference 到 Java 对象的映射。
native method 调用 DeleteLocalRef() 释放某个 JNI Local Reference 时,首先通过线程堆栈中的记录着 Local Reference 表的内存位置的指针定位相应的 Local Reference 在 Local Ref 表中的位置,然后从 Local Ref 表中删除该 Local Reference,也就取消了对相应 Java 对象的引用(Ref count 减 1)。
当越来越多的 LocalReference 被创建,这些 Local Reference 会在 Local Ref 表中占据越来越多内存。当 Local Reference 太多以至于 Local Ref 表的空间被用光,JVM 会抛出异常,从而导致 JVM 的崩溃。
产生Local Reference的操作有:
1.FindClass
2.NewString/ NewStringUTF/NewObject/NewByteArray
3.GetObjectField/GetObjectClass/GetObjectArrayElement
4.GetByteArrayElements和GetStringUTFChars
解决方法:
在native method中引用完java对象后及时调用env->DeleteLocalRef方法手动释放本地引用
如果native method返回java对象就不需要手动release,因为java会自动回收
但通过JNI传递对象数组时,由于需要在一个for循环中将C++对象数组成员中的每一个元素通过SetObjectField与java对象的元素进行对应,并调用SetObjectArrayElement将对象添加到数组中,期间可能会不断生成local reference,但是不能在循环中手动release,最终引起local reference内存泄露,因此针对与这种情况可以将对象数组分批传递
举例如下:
1、当java和c回调传的参数过多的时候,会出现内存泄露问题, 列如程序运行一段时间之后,莫名的出现如下错误JNI ERROR (app bug): local reference table overflow (max=512) Failed adding to JNI local ref table (has 512 entries) VM aborting
2、 引起这个bug的原因有如下几个:
上层传递参数String 给下层C语言,当底层使用完数据之后,一定要掉用ReleaseStringUTFChars接口将内存释放掉,不然当传递次数多了之后会导致系统奔溃
JNIEXPORT jint JNICALL test_string(JNIEnv *env, jobject obj,jstring j_usrname,jstring j_passwd,jint j_host_id) { int usr_id =1; const char *usrname = env->GetStringUTFChars (j_usrname, NULL); LOGD("usrname = %s",usrname); env->ReleaseStringUTFChars (j_usrname, usrname); return 1; }
3、底层jni里面C语言接口调用上层java的方法时候,一定要释放obj类,不然也会导致系统奔溃,如下列子:
底层子线程当中要调用上层的java的方法
3.1在cpp接口程序里面定义全局变量 JavaVM *g_jvm=NULL; jobject g_obj = NULL;
3.2在创建线程函数之前,给这两个变量赋值
JNIEXPORT jint JNICALL init(JNIEnv *env, jobject obj,jint mode) { env->GetJavaVM(&g_jvm); g_obj = env->NewGlobalRef(obj); pthread_t tid; pthread_create(&tid,NULL,testjni,NULL); }
3.3在线程函数里面调用上层的一个void fun(int a);方法 (注:只要在jni底层,除了自己用onload映射出来接口相对于整个APP来说是主线程函数以外,其它c函数接口都视为子线程函数)
void *testjni(void *arg) { JNIEnv *env; jmethodID met; jclass cls; while(1) { if(g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) { LOGD("%s: AttachCurrentThread() failed", __FUNCTION__); return NULL; } cls = env->GetObjectClass(g_obj); met =env->GetMethodID(cls, "fun","(I)V"); env->CallVoidMethod(g_obj, met, 10); env->DeleteLocalRef(g_obj); //注意必须释放缓存数据 sleep(1); } return NULL; }