JNI引用與緩存

全局引用/局部引用/弱全局引用

從java虛擬機創建的對象傳到本地c/c++代碼是會產生引用。根據java垃圾回收機制,只要有引用存在就不會觸發引用指向的java對象的垃圾回收。
這些引用在JNI中分三種:
1、全局引用(Global Reference)
2、局部引用(Local Reference)
3、弱全局引用(Weak Global Reference)
---------------------------------------
1、局部引用(常用)基本上通過JNI返回來的引用都是局部引用。
例如:NewObject就會返回創建出來的實例的局部引用。局部應用只在該native函數中有效。所有在該函數中產生的局部引用,都會在函數返回的時候自動釋放(freed),也可以使用DeleteLocalRef函數自動釋放該引用
在局部引用的有效期中,可以傳遞到別的函數中,要強調的是他的有效期仍然只在一次的java本地函數調用中,所以千萬不能用C++全局變量保存他或是把它定義爲C++靜態局部變量
2、全局引用
全局引用可以跨越當前線程,在多個native函數中有效,不過需要編程人員手動來釋放該引用,全局引用存在期間會防止在java的垃圾回收的回收。
與局部引用不同,全局引用的創建不由JNI自動創建的,全局引用是需要調用NewGlobealRef函數,而釋放他需要使用ReleaseGlobalRef函數。
3、弱全局引用
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);//刪除弱全局引用
jboolean IsSameObject(jobject obj1,jobject obj2);//判斷兩個引用是否指向同一個java對象
這個函數對於弱全局引用有一個特別的功能,把Null傳入要比較的對象中,就能夠判斷弱全局引用所指向的java對象是否被回收
----------------------------------------------------------------------
緩存:
緩存jfieldID/jmethodID
取得jfieldID跟jmethodID的時候會通過該屬性/方法名加上簽名來查詢相應的jfieldID/jmethodID,這種查詢相對開銷較大,我們可以將這些FieldID/MethodID緩存起來,這樣只需要查詢一次,以後就使用緩存起來的FieldID/MethodID了
緩存的方式:
1、在使用的時候緩存(Caching at the Point of Use)
2、在Java類初始化時緩存(Caching at the Defining Class's Inititalizer)
----------------
在第一次使用的時候緩存:
在native code中使用static局部變量來保存已經查詢過的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(class,"string","Ljava/lang/String;");
 }
 .......................
     }
---------------
在java類初始化的時候緩存
更好的方式就是在任何native函數調用前把id全部存起來。我們可以讓java在第一次加載這類的時候首先調用本地代碼初始化所有的jfieldID/jmethodID,這樣的話就可以省去多次的確定id是否存在的語句,當然,這些jfieldID/jmethodID是定義在c/c++的全局。
使用這種方式還有好處,當java類卸載或是重新加載的手也會重新呼叫該本地代碼來重新計算IDs。
例子:java類中:

複製代碼
public class TestNative
{
   static{ initNativeIDs();}
   static native void initNativeIDs();
   int propInt=0;
   String propStr="";
   public native void otherNative();
   public static void main(String[] args) {}
}
複製代碼

 

c/c++代碼中:

複製代碼
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_propStr_id=GetFieldID(clazz,"propStr","Ljava/lang/String;");
}
JNIEXPORT void JNICALL
Java_TestNative_otherNative(JNIEnv* env,jobject obj)
{
   //可以直接使用下g_propInt_id和g_propStr_id的值了
}
複製代碼

 

------------------------------------------------
使用JNI的兩個弊端
1、使用JNI,那麼這個Java Application將不能跨平臺,如果要移植別的平臺上,那麼native代碼就需要重新編寫。
2、java是強類型的語言,而c/c++不是,因此你必須在寫JNI時更小心
總之,必須在構建java程序的時候,儘量少使用本地代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章