zoukankan      html  css  js  c++  java
  • [Android] JNI中的Local Reference

    参考文章:《在 JNI 编程中避免内存泄漏

    1 Local Reference 深层解析

      JNI Local Reference 的生命期是在 native method 的执行期(从 Java 程序切换到 native code 环境时开始创建,或者在 native method 执行时调用 JNI function 创建),在 native method 执行完毕切换回 Java 程序时,所有 JNI Local Reference 被删除,生命期结束(调用 JNI function 可以提前结束其生命期)。

      (我们实际编码中经常会自己封装一个NewStringUTF、NewObject之类的引用方法,在同一个nativeMethod中反复多次调用,其实这样是误以为LocalReference与局部变量的生命周期一致了)  

      实际上,每当线程从 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。

    Java 代码部分
    class TestLocalReference { 
         private native void nativeMethod(int i); 
         public static void main(String args[]) { 
                TestLocalReference c = new TestLocalReference(); 
                //call the jni native method 
                c.nativeMethod(1000000); 
         }  
         static { 
             //load the jni library 
             System.loadLibrary("StaticMethodCall"); 
         } 
     } 
     
     JNI 代码,nativeMethod(int i) 的 C 语言实现
     #include<stdio.h> 
     #include<jni.h> 
     #include"TestLocalReference.h"
     JNIEXPORT void JNICALL Java_TestLocalReference_nativeMethod 
         (JNIEnv * env, jobject obj, jint count) 
     { 
         jint i = 0; 
         jstring str; 
     
         for(; i<count; i++) 
             str = (*env)->NewStringUTF(env, "0"); 
     } 
    
    运行结果
     JVMCI161: FATAL ERROR in native method: Out of memory when expanding 
     local ref table beyond capacity 
     at TestLocalReference.nativeMethod(Native Method) 
     at TestLocalReference.main(TestLocalReference.java:9)    
    View Code

    图中说明:

    ⑴ 运行 native method 的线程的堆栈记录着 Local Reference 表的内存位置(指针 p)。

    ⑵ Local Reference 表中存放 JNI Local Reference,实现 Local Reference 到 Java 对象的映射。

    ⑶ native method 代码间接访问 Java 对象(java obj1,java obj2)。通过指针 p 定位相应的 Local Reference 的位置,然后通过相应的 Local Reference 映射到 Java 对象。

    ⑷ 当 native method 引用一个 Java 对象时,会在 Local Reference 表中创建一个新 Local Reference。在 Local Reference 结构中写入内容,实现 Local Reference 到 Java 对象的映射。

    ⑸ native method 调用 DeleteLocalRef() 释放某个 JNI Local Reference 时,首先通过指针 p 定位相应的 Local Reference 在 Local Ref 表中的位置,然后从 Local Ref 表中删除该 Local Reference,也就取消了对相应 Java 对象的引用(Ref count 减 1)。

    ⑹ 当越来越多的 Local Reference 被创建,这些 Local Reference 会在 Local Ref 表中占据越来越多内存。当 Local Reference 太多以至于 Local Ref 表的空间被用光,JVM 会抛出异常,从而导致 JVM 的崩溃。

    2 关于JNI局部引用的释放

            对于FindClass 返回的一定需要调用DeleteLocalRef,还有jbyteArray 类型的变量需要DeleteLocalRef。NewString / NewStringUTF / NewObject / GetObjectField生成的需要DeleteLocalRef。以上返回的类型变量是malloc出来的,不是栈变量。出作用域不会被释放,需要手动。

  • 相关阅读:
    设计模式
    LintCode 数组(一)
    LintCode 字符串(二)
    LintCode 字符串(一)
    Java 多线程---volatile, ThreadLocal
    HTTP之缓存首部
    HTTP之首部
    HTTP之状态码
    web安全之XSS和CSRF
    web安全之同源策略
  • 原文地址:https://www.cnblogs.com/kuliuheng/p/8513360.html
Copyright © 2011-2022 走看看