jni全局引用/ 局部引用/(第三篇)

从 Java 虚拟机创建的对象传到本地 C/C++代码时会产生引用,根据 Java 的垃圾回收机制,
要有引用存在就不会触发该引用指向的 Java 对象的垃圾回收
·这些引用在 JNI 中分为三种
全局引用(Global Reference)
局部引用(Local Reference)
弱全局引用(Weak Global Reference)since JDK1.2
局部引用
·最常见的引用类型,基本上通过 JNI 返回来的引用都是局部引用
例如使用 NewObject 就会返回创建出来的实例的局部引用,局部引用只在该 native 函数
中有效,所有在该函数中产生的局部引用,都会在函数返回的时候自动释放(freed)也可以用
DeletLocalRef 函数手动释放该引用
·想一想既然局部引用能够在函数返回的时候自动释放,为什么还需要 DeleteLocalRef 函数
呢?
·实际上局部引用存在,就会防止其指向的对象被垃圾回收。尤其是当一个局部引用指向一
个很庞大的对象,或是在一个循环中生成了局部引用,最好的做法就是在使用完该对象后,
或在该循环的尾部把这个引用释放掉,以确保在垃圾回收器被触发的时候被回收
·在局部引用的有效期中,可以传递到别的本地函数中,要强调的是他的有效期仍然只是在
一次的 Java 本地函数中调用中,所以千万不能用 C++全局变量保存他或是把他定义为 C++静
态局部变量。
全局引用
·全局引用可以跨越当前线程,在多个 native 函数中有效,不过需要编程人员手动来释放该
引用,全局引用存在期间会防止在 Java 的垃圾回收的回收
·与局部引用不同,全局引用的创建不是由 JNI 自动创建的,全局引用是需要调用
NewGlobalRef 函数,而释放他需要使用 ReleaseGlobalRef 函数。
弱全局引用
·Java1.2 新出来的功能,与全局引用相似,创建跟删除都需要由编程人员来进行,这种引
用与全局引用一样可以在多个本地代码有效,也跨越多线程有效,不一样的是,这种引用将
不会阻止垃圾回收器回收这个引用所指向的对象。
·使用 NewWeakGlobalRef 跟 ReleaseWeakGlobalRef 来产生和解除引用
关于引用句的一些函数
·jobject NewGlobalRef(jobject obj);
·jobject NewLocalRef(jobject obj);
·jobject NewWeakGlobalRef(jobject obj);
·void DeleteGlobalRef(jobject obj);
·void DeleteLocalRef(jobject obj);
·void DeleteWeakGlobalRef(jobject obj);
·jobject IsSameObject(jobject obj1,jobject obj2);
//这个函数对于弱全局引用还有特别的功能,把 NULL 传入要比较的对象中,就能够了判断
弱全局引用所指向的 Java 对象是否被回收
缓存 jfieldID/jmethodID
·取得 jfieldID 跟 jmethodID 的时候都会通过该属性/方法名称加上签名来查询相应的
jfieldID/jmethodID。这种查询相对来说开销较大,我们可以将这些 FieldID/MethodID 缓存起
来,这样只需要查询一次,以后就使用方法缓存起来的 FieldID/MethodID 了。
·下面介绍两种存在方式:
1、 在用的时候缓存:
2、 在 Java 类初始化的时候缓存
缓存 fieldID/jmethodID, 在第一次使用的时候
·在 native code 中使用 static 局部变量来保存已经查询过的 id,这样就不会在每次的函数调
用时查询,而只要第一次查询成功后就保存起来了。
·不过在这种情况下就不得不考虑多线程同时呼叫此函数时可能会招致同时查询的危机,不
过这种情况是无害的,因为查询同一个属性/方法的 id 通过返回的是一样的值。
JNIEXPORT void JNICALL Java_Test_native(JNIEnv *env ,jobject obj){
static jfieldID fieldID_string = NULL;
jclass clazz = env->GetObjectClass(obj);
if(fieldID_string ==NULL){
fieldID_string = env->GetFieldID(clazz,”string”,”Ljava/lang/String”);
}
//….Other codes
}
上面的 fieldID 用了 static 关键字,这样第二次如果有值就不会再查询了。
缓存 fieldID/jmethodID,在 在 Java  对象初始化的时候
·更好的一个方式就是在任何 native 函数调用前把 id 全部存起来。
·我们可以让 Java 在第一次加载这个类的时候首先调用本地代码初始化所有的
jfieldID/jmethodID,这样的话就可以省去多次确定 id 是否存在的语句,当然这些
jfieldID/jmethodID 是定义在 C/C++的全局
·使用这种方式还有好处是:当 Java 类卸载或是重新加载的时候也会重新呼叫该本地代码来
重新计算 id。
Java  的类中:
Public class TestNative{
Static{
initNativeIDs();
}
Static native void initNativeIDs();
Int propInt = 0;
String propStr = “”;
Public native void otherNative();
C/C++ 代码中:
//global variables
jfieldID g_propInt_id = 0 ;
jfieldID g_propStr_id = 0 ;
JNIEXPORT void JNICALL
Java_TestNative_initNativeIDs(JNIEnv*env,jobject clazz){
G_propInt_id = GetFieldID(clazz,”propInt”,”I”);
G_propInt_id =
GetFieldID(clazz,”propStr”,”L/java/lang/String”);
//Other codes
}
}
jNIEXPORT void JNICALL
Java_TestNative_oherNative(JNIEnv *env,jobject obj){
//get field with g_propInt_id/g_propStr_id…
}
其他:
·异常处理
·C/C++如何启动 JVM
·JNI 跟多线程

猜你喜欢

转载自blog.csdn.net/u014644594/article/details/82769774