JNI中的引用类型分为三种:局部引用,全局引用,弱全局引用
- 局部引用
大多数JNI函数会创建局部引用。比如,NewObject创建一个新实例并返回它的局部引用。局部引用只在创建它的函数返回之前有效,一旦函数返回,局部引用就会被释放。你不能将局部引用缓存在一个静态变量中以供下一次函数调用时使用。这里有两种使局部引用失效的方法,一个是JVM在本地方法返回时自动释放局部引用,另一种是程序员通过JNI函数DeleteLocalRef显式的管理局部引用的生命周期。既然JVM会自动释放所有的局部引用,为什么还要手动的管理?局部引用会在失效之前阻止GC释放资源,JVM只能等到调用的函数返回后才能释放资源,而DeleteLocalRef则可以立即释放,并且局部引用数量有限制超过512个局部引用会报错。所以用完即时释放是比较保险的做法。局部引用只在创建它的线程中有效。不能使用全局变量来存储局部引用然后将其传递给其他的线程。
一般局部变量都是局部引用,不必强行指定为局部引用。
jclass cls = env->FindClass("java/lang/String");
jobject localRef = env->NewLocalRef(cls);//没必要⚠️
jmethodID jmid = env->GetMethodID((jclass)localRef, "<init>", "(Ljava/lang/String;)V");
jstring jstr = env->NewStringUTF("Hello Local Ref");
//需要释放局部引用时调用 env->DeleteLocalRef(localRef);
jstring ret = (jstring)env->NewObject((jclass)localRef, jmid, jstr);
- 全局引用
JNI函数在返回后,局部引用终究是会被释放。然而有时候我们需要把Java层传下来的对象进行保存,然后在函数返回后的某个时刻再进行操作。此时就需要为这个对象创建一个全局引用(例如:需要加一个回调,之后执行回调给Java层)。全局引用可以跨线程使用,在程序员显式的释放之前,它都是有效的。和局部引用一样,全局引用也会阻止GC释放资源。但和局部引用能被绝大多数JNI函数创建不一样的是,全局引用只能被jobject NewGlobalRef(JNIEnv *env, jobject obj);
创建。参数 obj 可以是一个局部引用,也可以是一个全局引用。这个函数执行成功会返回一个引用,然而如果发生OOM,返回NULL。全局引用在不需要的时候,必须手动释放,使用的函数原型如下void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
。例如下面全局引用string的jclass对象,第二次开始可以避免查找。平时工作中例如要实现一个回调。c++lamda表达式中需要引用一个java'对象的话需要用全局引用再让lamda持有,否则下次来调用是java对象已经失效了。
static jclass stringClass = nullptr;
if (stringClass == nullptr)
{
jclass cls = env->FindClass("java/lang/String");
stringClass = static_cast<jclass>(env->NewGlobalRef(cls));
//所有局部变量默认是局部引用
env->DeleteLocalRef(cls);
}
else
{
LOGI("string class 存在!");
}
//带参构造函数
jmethodID jmid = env->GetMethodID(stringClass, "<init>", "(Ljava/lang/String;)V");
jstring jstr = env->NewStringUTF("Hello Global Ref");
//global ref 在程序员确定用不上之后 需要删除
jstring ret = (jstring)env->NewObject(stringClass, jmid, jstr);
*弱全局引用
弱全局引用(Weak Global References)是一种特殊的全局引用,当一个底层Java对象只被弱全局引用所指向,这个弱全局引用不会阻止垃圾收集器回收这个底层Java对象。弱全局引用的目的很显示,那就是不阻止垃圾回收,一般是为了防止内存泄露。弱全局引用也需要一定的虚拟机资源,因此在不需要弱全局引用时,需要使用如下函数释放。void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);
。
typedef _jobject* jobject;
typedef _jobject* jweak;
jobject和jweak都是指针,而且都指向同一种类型的对象,因此把jweak当作jobject来用就行了。可以通过jboolean b = env->IsSameObject(mObject, NULL);
来检查指向的java对象是否被释放。由于弱全局引用无法阻止垃圾回收,为了在JNI函数中安全使用这个弱全局引用,可以把它转化为局部引用,这样就能在函数返回前,暂时阻止垃圾回收,于是可以安全操作底层的Java对象。
网友评论