(三)JNI 引用、異常處理和緩存策略

溫馨提示

寫博客是為了記錄在開發(fā)過程中所涉及到的技術(shù)以及遇到的問題的解決观挎,如果該博客對您有所幫助琴儿,希望可以點個關(guān)注/喜歡;如果您對文章中的內(nèi)容有什么不同的見解嘁捷,歡迎留言進行討論造成。謝謝!

JNI 引用雄嚣、異常處理和緩存策略

一晒屎、JNI 引用變量

1、引用類型

JNI 引用的類型分為局部引用和全局引用

2缓升、引用的作用

在JNI中告知虛擬機何時回收一個 JNI 變量

3鼓鲁、局部引用的使用

通過DeleteLocalRef 手動釋放

  1. 訪問一個很大的java對象,使用完成之后港谊,還要進行復(fù)雜的耗時操作
  2. 創(chuàng)建了大量的局部引用骇吭,占用了太多的內(nèi)存,而且這些局部引用跟后面的操作沒有關(guān)聯(lián)性歧寺。

例如:

  • 編寫JNITest.java文件
public class JNITest {
    
    public native void localRef();

    public static void main(String[] args){
        JNITest t = new JNITest();
        t.localRef();
    }

    
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件聲明對應(yīng)的方法燥狰;簽名可通過下面的命令獲取
javap -s -p com.example.jni.JNITest

產(chǎn)生的結(jié)果中有對應(yīng)的屬性和方法的簽名

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    localRef
 * Signature: 
 */
JNIEXPORT void JNICALL Java_com_example_jni_JNITest_localRef
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件實現(xiàn)com_example_jni_JNITest.h中聲明的方法
#include "com_example_jni_JNITest.h"
#include <stdlib.h>

int compare(int *a,int *b){
    return (*a) - (*b);
}

//模擬:循環(huán)創(chuàng)建數(shù)組 
JNIEXPORT void JNICALL Java_com_example_jni_JNITest_localRef
(JNIEnv *env, jobject jobj) {
    int i = 0;
    for(; i < 5; i++){
        //創(chuàng)建Date對象
        jclass cls = (*env)->FindClass(env,"java.util.Date");
        jmethodID constructor_mid = (*env)->GetMethodID(env,cls,"<init>","()V");
        jobject obj = (*env)->NewObject(env,cls,constructor_mid);
        //此處省略100行代碼
        //不再使用obj對象了
        //通知垃圾回收器回收這些對象
        (*env)->DeleteLocalRef(env,obj);
        //此處省略100行代碼
    }
}

4、全局引用的使用

全局引用可以共享(跨多個方法斜筐,多個線程)龙致,手動控制內(nèi)存使用(不再使用時通過 DeleteGlobalRef 手動釋放)

例如:

  • 編寫JNITest.java文件
public class JNITest {
    
    public native void createGlobalRef();
    
     public native String getGlobalRef();
     
     public native void deleteGlobalRef();

    public static void main(String[] args){
        JNITest t = new JNITest();
        t.createGlobalRef();
        t.getGlobalRef();
        t.deleteGlobalRef();
    }

    
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件聲明對應(yīng)的方法;簽名可通過下面的命令獲取
javap -s -p com.example.jni.JNITest

產(chǎn)生的結(jié)果中有對應(yīng)的屬性和方法的簽名

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    createGlobalRef
 * Signature: 
 */
JNIEXPORT void JNICALL Java_com_example_jni_JNITest_createGlobalRef
(JNIEnv *, jobject);

/*
 * Class:     com_example_jni_JNITest
 * Method:    getGlobalRef
 * Signature: 
 */
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_getGlobalRef
(JNIEnv *, jobject);

/*
 * Class:     com_example_jni_JNITest
 * Method:    deleteGlobalRef
 * Signature: 
 */
JNIEXPORT void JNICALL Java_com_example_jni_JNITest_deleteGlobalRef
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件實現(xiàn)com_example_jni_JNITest.h中聲明的方法
#include "com_example_jni_JNITest.h"
#include <stdlib.h>

//全局引用
jstring global_str;

JNIEXPORT void JNICALL Java_com_example_jni_JNITest_createGlobalRef
(JNIEnv *env, jobject jobj) {
    jstring obj = (*env)->NewStringUTF(env,"JNI development is powerful!");
    global_str = (*env)->NewGlobalRef(env,obj);
}



JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_getGlobalRef
(JNIEnv *env, jobject jobj) {
    return global_str;
}

JNIEXPORT void JNICALL Java_com_example_jni_JNITest_deleteGlobalRef
(JNIEnv *env, jobject jobj) {
    (*env)->DeleteGlobalRef(env,global_str);
}

5顷链、弱全局引用的使用

弱全局引用可以節(jié)省內(nèi)存目代,在內(nèi)存不足時可以釋放所引用的對象,可以引用一個不常用的對象嗤练,如果為NULL榛了,再臨時創(chuàng)建

創(chuàng)建:NewWeakGlobalRef

銷毀:DeleteGlobalWeakRef

二、JNI 的異常處理

JNI自己拋出的異常潭苞,在java層無法被捕獲忽冻,只能在C層清空;用戶通過ThrowNew拋出的異常此疹,可以在Java層捕獲

  1. 保證java代碼可以繼續(xù)運行
  2. 補救措施,保證 C 代碼繼續(xù)執(zhí)行

例如:

  • 編寫JNITest.java文件
public class JNITest {
    private String key="World!";
    
    public native void exception();

    public static void main(String[] args){
        JNITest t = new JNITest();
        t.exception();
    }

    
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件聲明對應(yīng)的方法;簽名可通過下面的命令獲取
javap -s -p com.example.jni.JNITest

產(chǎn)生的結(jié)果中有對應(yīng)的屬性和方法的簽名

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    exception
 * Signature: 
 */
JNIEXPORT void JNICALL Java_com_example_jni_JNITest_exception
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件實現(xiàn)com_example_jni_JNITest.h中聲明的方法
#include "com_example_jni_JNITest.h"
#include <stdlib.h>


JNIEXPORT void JNICALL Java_com_example_jni_JNITest_exception
(JNIEnv *env, jobject jobj) {
   jclass cls = (*env)->GetObjectClass(env,jobj);
   jfieldID fid = (*env)->GetFieldID(env,cls,"key2","Ljava/lang/String;");
   //檢測是否發(fā)生java異常
   jthrowable exception = (*env)->ExceptionOccurred(env);
   if(exception != NULL){
       //讓java代碼可以繼續(xù)運行
       //清空異常信息
       (*env)->ExceptionClear(env);
       fid = (*env)->GetFieldID(env,cls,"key","Ljava/lang/String;");
   }
   jstring jstr = (*env)->GetObjectField(env,jobj,fid);
   char *str = (*env)->GetStringUTFChars(env,jstr,NULL);
   //比對屬性值是否合法蝗碎,i 忽略大小寫
   if(_stricmp(str,"Hello World!")!=0){
       //人為拋出異常湖笨,交給Java層處理
       jclass newExCls = (*env)->FindClass(env,"java/lang/IllegalArgumentException");
       (*env)->ThrowNew(env,newExCls,"Key's value is invalid!");
   }
   
}

三、JNI 緩存策略

1蹦骑、局部的靜態(tài)變量慈省,當程序運行結(jié)束之后,變量的值還會在內(nèi)存中

例如:

  • 編寫JNITest.java文件
public class JNITest {
    
    private String key="Hello World!";
    public native String cached();

    public static void main(String[] args){
        JNITest t = new JNITest();
        for (int 1 = 0; i<100; i++){
           System.out.pringln("第"+(i+1)+"次執(zhí)行眠菇,結(jié)果為:"+t.cached()); 
        }
    }

    
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件聲明對應(yīng)的方法边败;簽名可通過下面的命令獲取
javap -s -p com.example.jni.JNITest

產(chǎn)生的結(jié)果中有對應(yīng)的屬性和方法的簽名

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    cached
 * Signature: 
 */
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_cached
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件實現(xiàn)com_example_jni_JNITest.h中聲明的方法
#include "com_example_jni_JNITest.h"
#include <stdlib.h>


JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_cached
(JNIEnv *env, jobject jobj) {
   jclass cls = (*env)->GetObjectClass(env,jobj);
   static jfieldID key_id = NULL;
   //獲取jfieldID只獲取一次
   if(key_id == NULL){
       key_id = (*env)->GetFieldID(env,cls,"key","Ljava/lang/String;");
       printf("------------GetFieldID--------\n")
   }
}

2、全局變量捎废,動態(tài)庫加載完成之后笑窜,立刻緩存起來

//初始化全局變量
jfieldID key_fid;
jmethodID random_mid;
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_initIds
(JNIEnv *env, jclass jcls) {
    key_fid = (*env)->GetFieldID(env,jcls,"key","Ljava/lang/String;");
    random_mid = (*env)->GetMethodID(env,jcls,"getRandomInt","(I)I");
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市登疗,隨后出現(xiàn)的幾起案子排截,更是在濱河造成了極大的恐慌,老刑警劉巖辐益,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件断傲,死亡現(xiàn)場離奇詭異,居然都是意外死亡智政,警方通過查閱死者的電腦和手機认罩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來续捂,“玉大人猜年,你說我怎么就攤上這事〖踩蹋” “怎么了乔外?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長一罩。 經(jīng)常有香客問我杨幼,道長,這世上最難降的妖魔是什么聂渊? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任差购,我火速辦了婚禮,結(jié)果婚禮上汉嗽,老公的妹妹穿的比我還像新娘欲逃。我一直安慰自己,他們只是感情好饼暑,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布稳析。 她就那樣靜靜地躺著洗做,像睡著了一般。 火紅的嫁衣襯著肌膚如雪彰居。 梳的紋絲不亂的頭發(fā)上诚纸,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音陈惰,去河邊找鬼畦徘。 笑死,一個胖子當著我的面吹牛抬闯,可吹牛的內(nèi)容都是我干的井辆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼溶握,長吁一口氣:“原來是場噩夢啊……” “哼杯缺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起奈虾,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤夺谁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后肉微,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體匾鸥,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年碉纳,在試婚紗的時候發(fā)現(xiàn)自己被綠了勿负。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡劳曹,死狀恐怖奴愉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铁孵,我是刑警寧澤锭硼,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏振愿。R本人自食惡果不足惜姊途,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一贺辰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸廊镜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嗤朴。三九已至配椭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間播赁,已是汗流浹背颂郎。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工吼渡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留容为,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓寺酪,卻偏偏與公主長得像坎背,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子寄雀,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容