JNI ERROR (app bug): local reference table overflow (max=512)

来源:互联网 发布:淘宝手机端怎么排名 编辑:程序博客网 时间:2024/05/22 14:19

      最近在做一个串口协议的jni接口,主要功能就是往串口发送规定协议的命令码,然后异步读取串口返回信息,按照规定协议解析数据,并回调java回调接口,返回解析出来的数据。

      调试的过程中,发现平凡调用接口是会出现以下异常信息:


       看错误信息,应该本地指针块最大只要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; }

0 0
原创粉丝点击