傳智播客JNI第七講 – JNI中的全局引用/局部引用/弱全局引用、緩存jfieldID和jmethodID的兩種方式

 講解JNI中的全局引用/局部引用/弱全局引用、緩存jfieldID和jmethodID的兩種方式,並編寫兩種緩存方式的示例代碼。java

1.從Java虛擬機建立的對象傳到本地C/C++代碼時會產生引用,根據Java的垃圾回收機制,只要有引用存在就不會出發該引用指向的Java對象的垃圾回收。c++

2.這些引用在JNI中分爲三種:
  全局引用:Global Reference
  局部引用:Local Reference
  若全局引用:Weak Global Reference since JDK1.2編程

3.局部引用
  1)最多見的引用類型,基本上經過JNI返回來的引用都是局部引用。例如使用NewObject就會返回建立出來的實例的局部引用,局部引用只在該native函數中有效,全部在該函數中產生的局部引用,都會在函數返回的時候自動釋放,也可使用DeleteLocalRef函數手動釋放該引用。
  2)想想既然局部引用可以在函數返回時自動釋放,爲何還須要DeleteLocalRef函數呢?
  3)實際上,局部引用存在,就會防止其指向的對象被垃圾回收,尤爲是當一個局部引用指向一個很龐大的對象,或是在一個循環中生成了局部引用,最好的作法就是在使用完該對象後,或在循環尾部把這個引用釋放掉,以確保在垃圾回收器被處罰的時候被回收。
  4)在局部引用的有效期中,能夠傳遞到別的本地函數中,要強調的是他的有效期仍然只在一次的Java本地函數調用中,因此千萬不能用C++全局變量保存它或者把它定義爲C++靜態局部變量。數組

4.全局引用
  1)全局引用能夠跨越當前線程,在多個native函數中有效,不過須要編程人員手動來釋放該引用,全局引用存在期間會防止在Java的垃圾回收。
  2)與局部引用不一樣,全局引用的建立不是由JNI自動建立的,全局引用時須要調用NewGlobalRef函數,而釋放它須要使用ReleaseGlobalRef函數。緩存

5.弱全局引用
  1)Java1.2新出來的功能,與全局引用類似,建立跟刪除都須要由編程人員來進行。這種引用與全局引用同樣能夠再多個本地代碼有效,也跨越多線程有效,不同的是,這種引用將不會阻止垃圾回收器回收這個引用所指向的對象。
  2)使用NewWeakGlobalRef跟ReleaseWeakGlobalRef來產生和解除引用。多線程

6.關於引用的一些函數
  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); // 這個函數對於弱全局引用還有一個特別的功能,把NULL傳入要比較的對象中,就可以判斷弱全局引用所指向的Java對象是否被回收。ide

7.緩存jfieldID,jmethodID
  1)取得jieldID跟jmethodID的時候會經過該屬性、方法名稱加上簽名來查詢相應的jfieldID,jmethodID。這種查詢相對來講開銷較大,咱們能夠將這些FieldID,MethodID緩存起來,這樣只須要查詢一次,之後就使用緩存起來的FieldID,MethodID。
  2)介紹兩種緩存方式
   1.在用的時候緩存 
   2.在Java類初始化時緩存函數

  11)在第一次使用的時候緩存
     在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 code...學習

       }
 
     22)在Java類初始化的時候緩存
        更好的一個方式就是在任何native函數調用前把id所有存起來。
 咱們可讓Java在第一次加載這個類的時候首先調用本地代碼初始化全部的jfieldID,jmethodID,這樣的話,就能夠省去屢次的肯定id是否存在的語句,固然,這些jfieldID,jmethodID是定義在C/C++的全局。
 使用這種方式的好處,當Java類卸載或是從新加載的時候,也會從新呼叫該本地代碼來從新計算IDs。ui

 

 

 

課程最後總結
在這一課中,咱們學習了:
1.最簡單的Java調用C/C++函數的方法
2.取得方法、屬性的ID,學會了取得/設置屬性,還有Java函數的調用。
3.Java/c++之間的字符串的轉換問題。
4.在C/C++下如何操做Java數組。
5.三種引用方式
6.如何緩存屬性和方法的ID

使用JNI的兩個弊端
1.使用了JNI,那麼這個應用就不能跨平臺了,若是須要移植到別的平臺上,那麼native代碼就須要從新編寫。
2.Java是強類型的語言,而C/C++不是,你必須寫JNI時更當心。
3.儘可能少使用本地代碼。

其它 1.異常處理 2.C/C++如何啓動JVM 3.JNI跟多線程     介紹兩本書做爲參考:   1)The Java Native Interface Programmer's Guide and Specification   2))JNI++ User Guide

相關文章
相關標籤/搜索