<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JNI:全局引用&局部引用&弱全局引用

          共 3192字,需瀏覽 7分鐘

           ·

          2022-03-06 23:15

          從Java虛擬機(jī)創(chuàng)建的對(duì)象傳到本地 C/C++ 代碼時(shí)就會(huì)產(chǎn)生引用。根據(jù)Java的垃圾回收機(jī)制,只要有引用存在就不會(huì)觸發(fā)該引用指向的Java對(duì)象的垃圾回收。這些引用在 JNI 中分為三種

          1. 全局引用 (Global Reference)

          2. 局部引用 (Local Reference)

          3. 弱全局引用 (Weak Global Reference), JDK 1.2 引入

          1. 局部引用
          • 最常見的引用類型,基本上通過JNI返回來的引用都是局部引用

          例如,使用NewObject就會(huì)返回創(chuàng)建出來的實(shí)例的局部引用。局部引用只在該native函數(shù)中有效,所有在該函數(shù)中產(chǎn)生的局部引用,都會(huì)在函數(shù)返回的時(shí)候自動(dòng)釋放(freed)。也可以使用DeleteLocalRef函數(shù)進(jìn)行手動(dòng)釋放該引用。

          • 想一想既然局部引用能夠在函數(shù)返回時(shí)自動(dòng)釋放,為什么還需要DeleteLocalRef函數(shù)呢?

          實(shí)際上局部引用存在,就會(huì)防止其指向的對(duì)象被垃圾回收。尤其是當(dāng)一個(gè)局部引用指向一個(gè)很龐大的對(duì)象,或是在一個(gè)循環(huán)中生成了局部應(yīng)用;最好的做法就是在使用完該對(duì)象后,或在該循環(huán)尾部把這個(gè)引用釋放掉,以確保在垃圾回收器被觸發(fā)的時(shí)候被回收。

          • 在局部引用的有效期中,可以傳遞到別的本地函數(shù)中,要強(qiáng)調(diào)的是它的有效期仍然只在一次的Java本地函數(shù)調(diào)用中,所以千萬不能用C++全局變量保存它或是把它定義為C++靜態(tài)局部變量。

          2. 全局引用
          • 全局引用可以跨越當(dāng)前線程,在多個(gè)native函數(shù)中有效,不過需要編程人員手動(dòng)來釋放該引用。全局引用存在期間會(huì)防止在Java的垃圾回收的回收。

          • 與局部引用不同,全局引用的創(chuàng)建不是由 JNI 自動(dòng)創(chuàng)建的,全局引用需要調(diào)用 NewGlobalRef 函數(shù),而釋放它需要使用 ReleaseGlobalRef 函數(shù)。

          3. 弱全局引用

          弱全局應(yīng)用是 JDK 1.2 新出來的功能,與全局引用相似,創(chuàng)建跟釋放都需要由編程人員來進(jìn)行操作。這種引用與全局引用一樣可以在多個(gè)本地代碼有效,也可以跨越多線程有效;不一樣的是,這種引用將不會(huì)阻止垃圾回收器回收這個(gè)引用所指向的對(duì)象。

          使用 NewWeakGlobalRef 跟 ReleaseWeakGlobalRef 來產(chǎn)生和釋放應(yīng)用。

          4. 關(guān)于引用的一些函數(shù)
          jobject NewGlabalRef(jobject obj);jobject NewLocalRef(jobject obj);jobject NewWeakGlobalRef(jobject obj);
          void DeleteGlobalRef(jobject obj);void DeleteLocalRef(jobject obj);jboolean IsSameObject(jobject obj1, jobject obj2);

          IsSameObject 函數(shù)對(duì)于弱引用全局應(yīng)用還有一個(gè)特別的功能,把NULL傳入要比較的對(duì)象中,就能夠判斷弱全局引用所指向的Java對(duì)象是否被回收。

          5. 緩存jfieldID / jmethodID

          獲取 jfieldID與jmethodID 的時(shí)候會(huì)通過該屬性/方法名稱加上簽名來查詢相應(yīng)的 jfieldID/jmethodID。這種查詢相對(duì)來說開銷較大。在開發(fā)中可以將這些 FieldID/MethodID 緩存起來,這樣就只需要查詢一次,以后就使用緩存起來的 FieldID/MethodID。

          • 下面介紹兩種緩存方式

          1. 在使用時(shí)緩存 (Caching at the Point of Use)

          2. 在Java類初始化時(shí)緩存 (Caching at the Defining Class's Inititalizer)

          5.1 在使用時(shí)緩存

          在native 代碼中使用static局部變量來保存已經(jīng)查詢過的jfieldID/jmethodID ,這樣就不會(huì)在每次的函數(shù)調(diào)用時(shí)查詢,而只要一次查詢成功后就保存起來了。

          JNIEXPORT void JNICALL Java_Test_native( JNIEnv* env, jobject ojb) {static jfieldID fieldID_str = NULL;   jclass clazz = env->GetObjectClass( obj );???if(fieldID_str?==?NULL){       fieldID_str = env->GetFieldID(clazz, "strField", "Ljava/lang/String");   }     //TODO Other codes}

          不過這種情況下,就不得不考慮多線程同時(shí)調(diào)用此函數(shù)時(shí)可能導(dǎo)致同時(shí)查詢的并發(fā)問題,不過這種情況是無害的,因?yàn)椴樵兺粋€(gè)屬性或者方法的ID,通常返回的值是一樣的。

          5.2 在Java類初始化時(shí)緩存

          • 更好的一個(gè)方式就是在任何native函數(shù)調(diào)用之前把id全部緩存起來。

          • 可以讓Java在第一次加載這個(gè)類的時(shí)候,首先調(diào)用本地代碼初始化所有的 jfieldID/jmethodID,這樣的話就可以省去多次判斷id是否存在的冗余代碼。當(dāng)然,這些 jfieldID/jmethodID 是定義在C/C++ 的全局。

          • 使用這種方式還有好處,當(dāng)Java類卸載或者重新加載的時(shí)候,也會(huì)重新調(diào)用該本地代碼來重新計(jì)算IDs。

          java代碼

          public class TestNative {
          static { initNativeIDs(); }
          static native void initNativeIDs();
          int propInt =0;
          String propStr = "";
          ???public?native?void?otherNative();
          //TODO Other codes }

          C/C++ 代碼

          //global variablesjfieldID g_propInt_id = 0;jfieldID g_propStr_id = 0;

          JNIEXPORT void JNICALL Java_TestNative_initNativeIDs( JNIEnv* env, jobject clazz){ g_propInt_id = env->GetFieldID(clazz, "propInt", "I"); g_propStr_id = env->GetFieldID(clazz, "propStr", "Ljava/lang/String;"); }

          JNIEXPORT void JNICALL Java_TestNative_otherNative( JNIEnv* env, jobject obj){ // TODO get field with g_propInt_id/g_propStr_id}
          6. 總結(jié)
          • 最簡單的Java調(diào)用C/C++函數(shù)的方法

          • 獲取方法/屬性的ID;學(xué)會(huì)了獲取/設(shè)置屬性;還有Java函數(shù)的調(diào)用

          • Java/C++之間的字符串的轉(zhuǎn)換問題

          • 在C/C++下如何操作Java的數(shù)組

          • 三種引用方式

          • 如何緩存屬性/方法的ID

          7. 回顧
          • 使用了JNI,那么這個(gè)Java應(yīng)用將不能跨平臺(tái)了。如果要移植到別的平臺(tái)上,那么native代碼就需要重新進(jìn)行編寫

          • Java是強(qiáng)類型的語言,而C/C++不是。因此,必須在寫JNI時(shí)倍加小心

          • 總之,必須在構(gòu)建Java程序的時(shí)候,盡量不用或者少用本地代碼

          • 異常處理

          • C/C++ 如何啟動(dòng)JVM

          • JNI與多線程

          《The?Java?Native?Interface?Programmer's?Guide?and?Specification》《JNI++ User Guide》
          b8561c90f726c315de33b5b0ec23cbad.webp

          分享&在看

          瀏覽 70
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一级日韩| 天堂网一区二区三区 | 免费观看日韩a | 欧美一级电影在线播放 | 91在线一区二区三区 |