1 . JNI 引用类型 : JNI 中 定义了 八种 Java 基本数据类型 , 其余的 jobject , jarray , jxxxArray , jclass , jstring 等都是引用类型 ;
① 规律 : 除 八种 基本数据类型之外的都是引用数据类型 ;
② 都是 Java 引用数据类型 : 这些数据类型都是 C/C++ 中定义的 Java 引用数据类型 , 其本质是 C/C++ 环境中对应的 Java 数据类型 ;
要注意将 JNI 中的 Java 类型引用 , 与 C/C++ 指针区分开 , 两者概念不同 ;
2 . JNI 引用类型分为三类 :
① 局部引用 : 其只在作用域内有效 , 内存不可回收 ;
② 全局引用 : 全局有效 , 内存不可回收 ;
③ 全局弱引用 : 全局有效 , 内存不足时会被 JVM 回收 ;
内存不可回收 , 如果内存不足 , 会直接 OOM 内存溢出 ;
在 JNI 中一定要将 引用 和 指针 区分开 ;
引用 是 Java 语言中的概念 , 指针 是 C/C++ 中的概念 ;
JNI 中 Java 引用类型 也是使用 C/C++ 指针表示的 , 这个 变量 就有了 两重含义 , 即代表 Java 中的引用类型 , 又代表了 编程环境中的 指针 ;
这里注意 , 如果引用被 JVM 释放了 , 即使指针还有值 , 也是不能用于 JNI 中与 Java 引用类型 相关的方法的 ;
本博客只讨论引用相关的内容 ;
1 . 局部引用作用域 :
局部引用只能在当前作用域有效 ;
超出作用域 手动释放 上面 两种情况 都会导致 局部引用变量 失效 ;
2 . 局部引用作用范围 :
① 空间 : 不能 跨线程 , 跨方法调用 , 仅在本作用域有效 ;
② 时间 : 创建后可以使用 , 手动释放 或 作用域结束 引用被释放不可使用 ;
1 . 局部引用产生 与 释放 :
① 局部引用产生 : 使用 NewXXX / FindXXX 等 大多数 JNI 方法 默认创建的 Java 引用类型对象 都是局部引用 ;
② 局部引用释放 : 调用 DeleteLocalRef 方法 释放该局部引用 ;
2 . 局部引用的两种释放方式 :
① 自动释放 : 在方法作用域结束后 , JVM 自动释放上述 局部引用 变量 ;
② 手动释放 : 通过调用 DeleteLocalRef 方法手动释放 ;
3 . 局部引用推荐释放方式 :
① 内存角度考虑 : 局部引用 释放尽量灵活 , 不要等待自动释放 , 在使用完毕后 建议就手动释放 , 尽早回收内存 ;
② 自动释放情况 :如果该 引用 一直到最后都要使用 , 那么可以不进行手动释放 ;
③ 建议用法 : 局部引用建议都要手动释放 , 哪怕是在作用域最后 , 也要进行手动释放
局部引用代码示例 :
extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniLocalReferenceTest(JNIEnv *env, jobject instance) {
/*
局部引用
局部引用只能在当前作用域有效
超出作用域
手动释放
上面 两种情况 都会导致 该局部变量都会失效
局部引用作用范围 :
空间 : 不能 跨线程 , 跨方法调用 , 仅在本作用域有效
时间 : 创建后可以使用 , 手动释放 或 作用域结束 引用被释放不可使用
局部引用 创建 : 使用 NewXXX / FindXXX 等 大多数 JNI 方法 默认创建的都是局部引用
释放 : 调用 DeleteLocalRef 方法 释放该局部引用
关于上面的三个创建的 局部引用 有两种释放方式
方式一 : 在方法作用域结束后 , VM 自动释放上述变量
方式二 : 通过调用 DeleteLocalRef 方法手动释放
建议使用方式二 :
局部引用 释放尽量灵活 , 不要等待自动释放 , 在使用完毕后 建议就手动释放 , 今早回收内存
如果该 引用 一直到最后都要使用 , 那么可以不进行手动释放 ;
建议用法 : 局部引用建议都要手动释放 , 哪怕是在作用域最后 , 也要进行手动释放
局部引用传递到 Java 层 , 该传递是拷贝传递 , JNI 中该释放还是释放 , 不影响 Java 层使用
引用概念 :
这里要将 引用 和 指针的概区分清楚 ;
class_teacher 引用在 作用域结束时 会被释放 , 不能将其用于 JNI 反射 Java 类的方法和字段
其指针值不为空 , 仍然有值 , 其仍然指向一个地址 , 但是地址中的数据被释放了
*/
// 1 . 获取 Teacher 类 ( 该变量需要释放 )
jclass class_teacher = env->FindClass("kim/hsl/jni/Teacher");
// 2 . 查找构造方法
jmethodID method_init = env->GetMethodID(class_teacher, "<init>", "(ILjava/lang/String;)V");
// 3 . 准备 Java 类型参数 ( 该变量需要释放 )
// 此处特别注意 : 传入到 Java 方法中的参数都必须是 Java 参数
jint teacher_age = 88;
jstring teacher_name = env->NewStringUTF("Tom Wang");
// 4 . 创建 Teacher 对象 ( 该变量需要释放 )
jobject teacher = env->NewObject(class_teacher, method_init, teacher_age, teacher_name);
// 5 . 释放上面通过 FindClass NewStringUTF NewObject 创建的引用变量 , 否则会造成内存泄漏
// 使用完这三个引用之后 , 不再使用 ; 这里特别建议手动释放三个引用
// 如果不手动释放 , 在 该引用 作用域 结束后 , 也会自动释放掉
env->DeleteLocalRef(teacher_name);
env->DeleteLocalRef(teacher);
env->DeleteLocalRef(class_teacher);
}