溫馨提示
寫博客是為了記錄在開發(fā)過程中所涉及到的技術(shù)以及遇到的問題的解決观挎,如果該博客對您有所幫助琴儿,希望可以點個關(guān)注/喜歡;如果您對文章中的內(nèi)容有什么不同的見解嘁捷,歡迎留言進行討論造成。謝謝!
JNI 引用雄嚣、異常處理和緩存策略
一晒屎、JNI 引用變量
1、引用類型
JNI 引用的類型分為局部引用和全局引用
2缓升、引用的作用
在JNI中告知虛擬機何時回收一個 JNI 變量
3鼓鲁、局部引用的使用
通過DeleteLocalRef 手動釋放
- 訪問一個很大的java對象,使用完成之后港谊,還要進行復(fù)雜的耗時操作
- 創(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層捕獲
- 保證java代碼可以繼續(xù)運行
- 補救措施,保證 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");
}