美文网首页
JNI的引用问题

JNI的引用问题

作者: 明日即是今朝 | 来源:发表于2020-08-06 10:39 被阅读0次

    问题

    1.Java调用JNI的效率比Java调用java的效率要低很多(这个可以去测试对比下),而且JNI中的findClass()等方法比较耗时,不可能每次都去调用,怎么做缓存?
    2.上次博客遗留的问题,C给java传值的时候,直接return了数据,NewByteArray也没有造成内存的泄漏,这里面涉及到了什么?

    实践

    释放内存和局部引用

    我们先拿上次博客没说透的问题来看下。

    Java_xxxxx_PcmResample_resample(JNIEnv *env, jclass clazz, jbyteArray pcm) {
            //xxxx省略处理pcm的过程
            //buff 是处理的指针数据
            jbyte* buff;
            jbyteArray data = env->NewByteArray(in.size);
            env->SetByteArrayRegion(data, 0, in.size, buff);
            return data;
    }
    

    这里我直接返回了data,NewByteArray并没有造成内存泄漏,这是为什么?这里其实有两层问题。

    • 第一个是jbyteArray data 这个data需不需要释放,
    • 第二个是这里如果需要释放我们没有释放为什么也没出问题。
      首先明确一点,这个data是需要释放的,不然会造成内存的泄漏,在JNI中继承自jobject的都需要释放内存,包括各种jarray,jstring,jobject,以及jclass都是需要释放的。基本类型的不用释放jint,jfloat以及jmethodid,jfieldid等。这些变量都是可以被跨方法,跨线程共享的。
      那第二个问题,这里没出问题是因为涉及到了JNI中的一种引用,叫做局部引用。局部引用也就是在方法内部定义的变量,他在方法执行完后,会自动释放掉data 所指向的地址。这里每次返回data后,就会释放掉data,所以不会内存泄漏。
      那这两个问题弄明白了,我们发现JNI提供了一个删除局部引用的方法,DeleteLocalRef,既然都自己释放内存了为啥还需要提供这个方法?这里如果解释为就算系统自己释放了,自己释放下更保险这样的是不能完全让人接受的,所以他肯定是为了应对某种情况。我们看下下面的代码
    Java_xxxxx_PcmResample_resample(JNIEnv *env, jclass clazz, jbyteArray pcm) {
            //xxxx省略处理pcm的过程
            //buff 是处理的指针数据
          jbyte* buff;
          int i=0;
          while(i<1000000){
          jbyteArray data = env->NewByteArray(in.size);
            env->SetByteArrayRegion(data, 0, in.size, buff);
        }
          return data;
    }
    

    如果我们在while里面执行多次这个NewByteArray以后,我们就会发现程序崩溃了。崩溃的原因是因为local ref over flow 局部变量的表满了,每个进程对于局部变量都会维护一张表,局部变量的数量是有限的,如果超过了就崩溃,具体是多少我也没尝试,网上说是512。那这里因为程序迟迟不能出去,所以导致局部变量一直增加最终导致崩溃,所以这种情况下我们就需要手动的在循环里面调用DeleteLocalRef去释放内存了。

    Java_xxxxx_PcmResample_resample(JNIEnv *env, jclass clazz, jbyteArray pcm) {
            //xxxx省略处理pcm的过程
            //buff 是处理的指针数据
          jbyte* buff;
          int i=0;
          while(i<1000000){
          jbyteArray data = env->NewByteArray(in.size);
          env->SetByteArrayRegion(data, 0, in.size, buff);
          env->DeleteLocalRef(pArray);
        }
          return data;
    }
    

    缓存JNI数据

    对于基本数据类型我们把它放在类里面他就可以他直接可以跨方法跨线程使用,如果是jobject对象,例如jclass,为了防止每次都去读取耗费时间,我们需要把它缓存起来,这个时候就需要把它置为全局变量就可以,关于全局变量和弱全局变量,大家可以去看下这个博客,讲的很清楚,没有什么疑问点所以我就不重复了。
    https://blog.csdn.net/xyang81/article/details/44657385

    总结

    弄明白jni的引用问题很关键,因为这涉及到你编码时候对于内存使用的正确性,也涉及到整个程序的内存问题。
    水平有限,如有错误,请谅解,也请不吝赐教。

    相关文章

      网友评论

          本文标题:JNI的引用问题

          本文链接:https://www.haomeiwen.com/subject/dlabrktx.html