(三)JNI 引用、异常处理和缓存策略
2019-03-11 本文已影响5人
onestravel
JNI 引用、异常处理和缓存策略
一、JNI 引用变量
1、引用类型
JNI 引用的类型分为局部引用和全局引用
2、引用的作用
在JNI中告知虚拟机何时回收一个 JNI 变量
3、局部引用的使用
通过DeleteLocalRef 手动释放
- 访问一个很大的java对象,使用完成之后,还要进行复杂的耗时操作
- 创建了大量的局部引用,占用了太多的内存,而且这些局部引用跟后面的操作没有关联性。
例如:
- 编写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文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest
产生的结果中有对应的属性和方法的签名
/* 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文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"
#include <stdlib.h>
int compare(int *a,int *b){
return (*a) - (*b);
}
//模拟:循环创建数组
JNIEXPORT void JNICALL Java_com_example_jni_JNITest_localRef
(JNIEnv *env, jobject jobj) {
int i = 0;
for(; i < 5; i++){
//创建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、全局引用的使用
全局引用可以共享(跨多个方法,多个线程),手动控制内存使用(不再使用时通过 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文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest
产生的结果中有对应的属性和方法的签名
/* 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文件实现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、弱全局引用的使用
弱全局引用可以节省内存,在内存不足时可以释放所引用的对象,可以引用一个不常用的对象,如果为NULL,再临时创建
创建:NewWeakGlobalRef
销毁:DeleteGlobalWeakRef
二、JNI 的异常处理
JNI自己抛出的异常,在java层无法被捕获,只能在C层清空;用户通过ThrowNew抛出的异常,可以在Java层捕获
- 保证java代码可以继续运行
- 补救措施,保证 C 代码继续执行
例如:
- 编写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文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest
产生的结果中有对应的属性和方法的签名
/* 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文件实现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;");
//检测是否发生java异常
jthrowable exception = (*env)->ExceptionOccurred(env);
if(exception != NULL){
//让java代码可以继续运行
//清空异常信息
(*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、局部的静态变量,当程序运行结束之后,变量的值还会在内存中
例如:
- 编写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)+"次执行,结果为:"+t.cached());
}
}
static {
System.loadLibrary("JNIProject");
}
}
- 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest
产生的结果中有对应的属性和方法的签名
/* 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文件实现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、全局变量,动态库加载完成之后,立刻缓存起来
//初始化全局变量
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");
}