android 内存泄露之jni local reference table overflow (max=512)

在android项目中要实现一个需求

为了性能的要求只能用c代码来实现功能。

这样就牺牲了java跨平台性。

通过加载.so的方式,把用c实现的模块集成到app中。

android提供jni层,作为一个适配器。

可以在java层调用c接口,在jni层可以通过java提供的反射机制调用java接口和创建java对象。

最后需求完成了,自测也没问题,嘻嘻,自己也开心了一下,但是提交测试后,测试人员马上报了一个bug。

出现local reference table overflow (max=512)这样的一个错误。我去,尽然出现了崩溃。

google和百度了半天,才发现原来发生了jni层的内存泄露,导致了崩溃。

jni层到底出现了啥内存泄露????

从java代码进入jni层本地代码调用时,Dalvik就创建了一张local reference表来存储local reference,这张表

的表项数有最大值限制,一般最大为都是512个,local reference表只有当退出jni层代码的调用是才会清除掉。

当表项数超过最大值限制时,Dalvik就会抛出异常,导致了崩溃。

何为local reference????

他是在native代码层他是一个本地变量和java对象的引用。

JNIEXPORT jint JNICALL Java_com_print(JNIEnv *env, jobject obj){

  char data[] = "daffdasf";

    jstring content = (*env)->NewStringUTF(env, data);

}

变量content在 函数Java_com_print中是一个本地变量并且是String对象一个引用。所以会在local reference table

中追加一个表项来指向java对象。

其实每次进入native代码都会存在一个全局指向local reference table起始位置的ptr变量。

而上面函数中的content只是代表一个在local reference table中的偏移,通过ptr + content偏移

从local reference table获取java对象的值。

什么原因会发生local reference table overflow?????

那就是在一个循环中不断的创建local reference,而没有调用DeleteLocalRef去销毁这个local reference,

从而导致local reference table中表项不断增加,最后超过最大值,抱出了异常,导致了崩溃。

举两个例子哈:

例子1.

JNIEXPORT jint JNICALL Java_com_example(JNIEnv *env, jobject obj){

  char data[] = "daffdasf";

  int i = 0;

  for(i = 0;i < 1000;i++){

    jstring content = (*env)->NewStringUTF(env, data);

  }

}

例子1代码会导致local reference table overflow

例子2.

int Java_com_example(char * data){

   JNIEnv *env = NULL;
   JavaVM * vm = NULL;

  vm = getVm();

  (*vm)->AttachCurrentThread(vm, &env, NULL);

     jstring content = (*env)->NewStringUTF(env, data);

}

void callExample(){

  int i = 0;

  char data [] = "dafdfdasfds";

  for(i = 0;i < 1000;i++){

    Java_com_example(data);

  }

}

例子2代码会导致local reference table overflow

大家写jni的代码时要防止jni层的内存泄露,要注意用本地语言的方式清除本地语言获得的内存,

也要注意local reference和global reference的使用。

 

当然jni官文文档中没有告诉我们jni层实现的细节,只告诉我们如何规范的编写jni代码,这当然是正确的做法。

对于使用者来说只需要关注的他的使用不需要关注实现细节,这样就保证了可扩展性。

 

所以对于不同的对local reference的实现可能结果不一样,就有可能不出现上面local reference table overflow的错误了。

posted @ 2014-01-15 00:10  饮识止渴  阅读(7558)  评论(0编辑  收藏  举报