JNI引用類型
JNI提供了一些執行個體和數組類型(jobject、jclass、jstring、jarray等)作為不透明的引用供本地代碼使用。本地代碼永遠不會直接操作引用指向的VM内部的資料内容。要進行這些操作,必須通過使用JNI操作一個不透明的引用來間接操作資料内容。
- JNI支援三種引用:局部引用、全局引用、弱全局引用。
- 局部應用和全局引用有不同的生命周期。當本地方法傳回時,局部引用會被自動釋放。而全局引用和弱引用必須手動釋放。
- 局部引用或全局引用會阻止GC回收它們所引用的對象,而弱引用則不會。
- 不是所有的引用可以被用在所有的場合。例如:一個本地方法建立一個局部引用并傳回,再對這個局部引用進行通路是非法的。
局部引用和全局引用
局部引用
大多數JNI函數會建立局部引用。例如:NewObject建立一個新的對象執行個體并傳回一個對這個對象的局部引用。
局部引用隻有在建立它的本地方法傳回前有效。本地方法傳回後,局部引用會被自動釋放。
你不能在本地方法中國年把局部引用存儲在靜态變量中緩存起來供下一次調用時使用。
如:
strClass是一個java.lang.String對象的局部引用。當一個方法之行結束後,strclass會被VM釋放。當再次調用方法時,會試圖通路一個無效的局部引用,進而導緻非法的記憶體通路甚至崩潰。
釋放局部引用有兩種方式
1. 本地方法執行完後VM自動釋放。
2. 通過DeleteLocalRef手動釋放
全局引用
全局引用可以跨方法、線程使用,直到它呗手動釋放才會失效。同局部引用一樣,全局引用也會阻止它所引用的對象被GC回收。
與局部引用可以被大多數JNI函數建立不同,全局引用隻能使用一個JNI函數建立:NewGlobalRef。
下面用一個例子來介紹一個全局引用和局部引用的使用:
建立兩個native方法
//建立全局引用
public native void createGlobalRef();
//使用全局應用并釋放
public native String useGlobalRef(char[] chars);
生成相應頭檔案
JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_createGlobalRef
(JNIEnv *, jobject);
JNIEXPORT jstring JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_useGlobalRef
(JNIEnv *, jobject, jcharArray);
JNI方法實作
下面的方法主要實作的是全局引用的生成,以及局部引用的建立及釋放
//全局引用靜态變量
static jclass strGloClass = NULL;
JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_createGlobalRef
(JNIEnv *env, jobject jobj){
if(strGloClass == NULL){
//建立本地引用
jclass localRefCls = (*env).FindClass("java/lang/String");
if(localRefCls == NULL){
return; //exeption
}
//建立全局引用
strGloClass = (jclass) (*env).NewGlobalRef(localRefCls);
//當本地引用不用的時候,釋放掉
(*env).DeleteLocalRef(localRefCls);
//判斷全局引用是否建立成功
if(strGloClass == NULL){
return; //out of memory exception
}
}
};
全局引用的使用及釋放
JNIEXPORT jstring JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_useGlobalRef
(JNIEnv *env, jobject jobj, jcharArray chars){
//使用全局引用
jstring result = (jstring) (*env).NewObject(strGloClass, strInitID, chars);
//釋放全局引用(這個根據自己需要釋放,釋放以後不能再使用全局引用)
(*env).DeleteGlobalRef(strGloClass);
return result;
};
java中使用
Log.i(TAG, "初始化全局引用");
jd.createGlobalRef();
Log.i(TAG, "傳入:{'a', 'b', 'c'}");
Log.i(TAG, "使用全局引用,并釋放全局引用");
result = jd.useGlobalRef(chars);
Log.i(TAG, "輸出:" + result);
// result = jd.useGlobalRef(chars);
Log.i(TAG, "------------------------------------------");
輸出結果:
- :: -/com.test.git.jnidemo I/MainActivity-: 初始化全局引用
- :: -/com.test.git.jnidemo I/MainActivity-: 傳入:{'a', 'b', 'c'}
- :: -/com.test.git.jnidemo I/MainActivity-: 使用全局引用,并釋放全局引用
- :: -/com.test.git.jnidemo I/MainActivity-: 輸出:abc
- :: -/com.test.git.jnidemo I/MainActivity-: ------------------------------------------
tips
上面調用如果改成:
jd.createGlobalRef();
result = jd.useGlobalRef(chars);
Log.i(TAG, "輸出:" + result);
result = jd.useGlobalRef(chars);
則會報錯,因為全局變量被釋放後,不能再被使用。
弱引用
弱引用使用NewGlobalWeakRef建立,使用DeleteGlobalWeakRef釋放。
與全局引用類似,弱引用可以跨方法、線程使用。
與全局引用不同的是,弱引用不會阻止GC回收它所指向的VM内部對象。
當本地代碼中緩存的引用不一定要阻止GC回收它所指向的對象時,弱引用就說一個最好的選擇。
對于弱引用,我們使用時需要攀登緩存過的弱引用是指向活動類的對象,還是被GC回收。
引用比較
給定兩個引用,可以使用IsSameObject來判斷它們兩個是否指向相同的對象。
比如:
(*env).IsSameObject(obj1, obj2)
如果指向相同對象,則傳回JNI_TRUE(1)否則傳回JNI_FALSE(0)。
JNI中的一個引用NULL指向JVM中的null對象。如果obj是一個局部或者全局引用,可以使用
(*env).IsSameObject(obj, NULL)
或者obj == NULL來判斷obj是否指向一個null對象
在這一點上,弱引用有些不同。
一個NULL弱引用同樣指向一個JVM中的null對象。但不同的是,在一個弱引用上面使用IsSameObject時,傳回值的意義時不同的。
比如:
(*env).IsSameObject(wobj, NULL);
如果wobj已經被回收,會傳回JNI_TRUE,如果wobj仍然指向一個活動對象,會傳回JNI_FALSE。