JNI使用注意与避免内存泄露总结

  1. 说明:只是学习总结,没有深入分析,没有涉及JVM,内存归属等

  1.  原理:一套Java与本地C/C++/汇编交互规范,由JNIEnv实现。
  2. 优势:

    1. 利用 native code 的平台相关性,在平台相关的编程中彰显优势。
    2. 对 native code 的代码重用。
    3. native code 底层操作,更加高效。
  3. JNI类型映射表:

    表A

    Java 类型 本地类型 描述
    boolean jboolean C/C++8位整型
    byte jbyte C/C++带符号的8位整型
    char jchar C/C++无符号的16位整型
    short jshort C/C++带符号的16位整型
    int jint C/C++带符号的32位整型
    long jlong C/C++带符号的64位整型e
    float jfloat C/C++32位浮点型
    double jdouble C/C++64位浮点型
    Object jobject 任何Java对象,或者没有对应java类型的对象
    Class jclass Class对象
    String jstring 字符串对象
    Object[] jobjectArray 任何对象的数组
    boolean[] jbooleanArray 布尔型数组
    byte[] jbyteArray 比特型数组
    char[] jcharArray 字符型数组
    short[] jshortArray 短整型数组
    int[] jintArray 整型数组
    long[] jlongArray 长整型数组
    float[] jfloatArray 浮点型数组
    double[] jdoubleArray 双浮点型数组

    ※     JNI类型映射

  4. 用GetXXXArrayElements和ReleaseXXXArrayElements实现对基本类型数组操作,函数表:

    表B

    函数 Java 数组类型 本地类型
    GetBooleanArrayElements jbooleanArray jboolean
    GetByteArrayElements jbyteArray jbyte
    GetCharArrayElements jcharArray jchar
    GetShortArrayElements jshortArray jshort
    GetIntArrayElements jintArray jint
    GetLongArrayElements jlongArray jlong
    GetFloatArrayElements jfloatArray jfloat
    GetDoubleArrayElements jdoubleArray jdouble

    JNI数组存取函数

  5. 用GetObjectArrayElement函数和SetObjectArrayElement实现对象数组操作。
  6. 通个GetObjectClass或FindClass得到一个类。如jclass objClass = (env)->FindClass("java/lang/Object");
  7. 通过以下一些规则和方法操作一个对象:
  8. 表C

    函数 描述
    GetFieldID 得到一个实例的域的ID
    GetStaticFieldID 得到一个静态的域的ID
    GetMethodID 得到一个实例的方法的ID
    GetStaticMethodID 得到一个静态方法的ID

    ※域和方法的函数


    表D

    Java 类型 符号
    boolean Z
    byte B
    char C
    short S
    int I
    long L
    float F
    double D
    void V
    objects对象 Lfully-qualified-class-name;L类名
    Arrays数组 [array-type [数组类型
    methods方法 (argument-types)return-type(参数类型)返回类型

    ※确定域和方法的符号

  9. JNI使用N大注意点:
    A 一定要缓存方法 ID、字段 ID和类

    B 数组操作避免触发数组副本:GetTypeArrayRegion() 和 SetTypeArrayRegion() 方法允许您获取和更新数组的一部分,而不是整个数组。比较好的方法是在一个调用中获取大小合理的数组部分,然后再迭代所有这些元素,重复操作直到覆盖整个数组。

    C 传一个有多个字段的对象然后回访,比单独传递各个字段要慢很多。

    D 不要错误认定本机代码与 Java 代码之间的界限:在设计 Java 代码与本机代码之间的界限时应该最大限度地减少两者之间的相互调用。

    E 使用大量本地引用,而未通知 JVM:要及时DeleteLocalRef。

    F 使用错误的 JNIEnv:仅在相关的单一线程中使用 JNIEnv

    G 未检测异常:主动检查异常,if((*env)->ExceptionOccurred(env)) return;

    H 未检测返回值:调用任何一个方法后,先判断返回值,再继续往下。

    I 未正确使用数组方法: 记得调用ReleaseXXX(),否则对数组的更改不会被复制回去。然后确保代码不会在 GetXXXCritical() 和ReleaseXXXCritical() 调用之间发起任何 JNI 调用或由于任何原因出现阻塞。

    J 未正确使用全局引用:始终跟踪全局引用,并确保不再需要对象时删除它们。

    K 解决方法:
    根据规范验证新代码
    分析方法跟踪
    使用 -verbose:jni 选项
    生成转储
    执行代码审查

    L 如何避免GC:临界区的使用Critical region,Global Reference

    M 用JNI_OnLoad和JNI_UnLoad实现JNI版本管理与动态注册JNI方法(*env)->RegisterNatives

    jint JNI_OnLoad(JavaVM* vm, void* reserved)
    {
        JNIEnv* env = NULL;
        jclass clazz;
        //获取JNI环境对象
        if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
            LOGE("ERROR: GetEnv failed\n");
            return JNI_ERR;
        }
        //注册本地方法.Load 目标类
        clazz = (*env)->FindClass(env,classPathName);
        if (clazz == NULL)
        {
            LOGE("Native registration unable to find class '%s'", classPathName);
            return JNI_ERR;
        }
        //注册本地native方法
        if((*env)->RegisterNatives(env, clazz, methods, NELEM(methods)) < 0)
        {
            LOGE("ERROR: MediaPlayer native registration failed\n");
               return JNI_ERR;
        }
    
        /* success -- return valid version number */
        return JNI_VERSION_1_4;
    }

  10. 几点弊端:
    A 从 Java 环境到 native code 的上下文切换耗时、低效。
    B JNI 编程,如果操作不当,可能引起 Java 虚拟机的崩溃。
    C JNI 编程,如果操作不当,可能引起内存泄漏。

  11. 避免Native memory内存泄露(JVM内存包括Java Heap与Nativememory):
    A  Native Code 本身的内存泄漏: 一定要遵循每门native 的编程语言自身的内存管理机制。
    B Global Reference 引入的内存泄漏:一定记得释放
    C JNI 编程中潜在的内存泄漏— LocalReference :LocalReference Table对LocalReference 总数有限制,同时切记一定LocalReference 的自动释放一定是JNI返回java层。
  12. 参考文档:  

                    A 官方技术文档:SUN JNI Tutorial, http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/jniTOC.html

                   B JNI中C++和Java的参数传递:http://www.blogjava.net/china-qd/archive/2006/04/29/44002.html

                   C 官方中文版:http://blog.csdn.net/a345017062/article/details/8068915  http://blog.csdn.net/a345017062/article/details/8068909

                   E 如何避免内存泄露:http://www.ibm.com/developerworks/cn/java/j-lo-jnileak/

                   F 使用 Java Native Interface 的最佳实践 http://www.ibm.com/developerworks/cn/java/j-jni/  http://blog.csdn.net/tomken_zhang/article/details/6890333


               
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值