JNI引用类型
JNI提供了一些实例和数组类型(jobject、jclass、jstring、jarray等)作为不透明的引用供本地代码使用。本地代码永远不会直接操作引用指向的VM内部的数据内容。要进行这些操作,必须通过使用JNI操作一个不透明的引用来间接操作数据内容。
- JNI支持三种引用:局部引用、全局引用、弱全局引用。
- 局部应用和全局引用有不同的生命周期。当本地方法返回时,局部引用会被自动释放。而全局引用和弱引用必须手动释放。
- 局部引用或全局引用会阻止GC回收它们所引用的对象,而弱引用则不会。
- 不是所有的引用可以被用在所有的场合。例如:一个本地方法创建一个局部引用并返回,再对这个局部引用进行访问是非法的。
局部引用和全局引用
局部引用
大多数JNI函数会创建局部引用。例如:NewObject创建一个新的对象实例并返回一个对这个对象的局部引用。
局部引用只有在创建它的本地方法返回前有效。本地方法返回后,局部引用会被自动释放。
你不能在本地方法中国年把局部引用存储在静态变量中缓存起来供下一次调用时使用。
如:
strClass是一个java.lang.String对象的局部引用。当一个方法之行结束后,strclass会被VM释放。当再次调用方法时,会试图访问一个无效的局部引用,从而导致非法的内存访问甚至崩溃。
释放局部引用有两种方式
1. 本地方法执行完后VM自动释放。
2. 通过DeleteLocalRef手动释放
全局引用
全局引用可以跨方法、线程使用,直到它呗手动释放才会失效。同局部引用一样,全局引用也会阻止它所引用的对象被GC回收。
与局部引用可以被大多数JNI函数创建不同,全局引用只能使用一个JNI函数创建:NewGlobalRef。
下面用一个例子来介绍一个全局引用和局部引用的使用:
创建两个native方法
//创建全局引用
public native void createGlobalRef();
//使用全局应用并释放
public native String useGlobalRef(char[] chars);
生成相应头文件
JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_createGlobalRef
(JNIEnv *, jobject);
JNIEXPORT jstring JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_useGlobalRef
(JNIEnv *, jobject, jcharArray);
JNI方法实现
下面的方法主要实现的是全局引用的生成,以及局部引用的创建及释放
//全局引用静态变量
static jclass strGloClass = NULL;
JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_createGlobalRef
(JNIEnv *env, jobject jobj){
if(strGloClass == NULL){
//创建本地引用
jclass localRefCls = (*env).FindClass("java/lang/String");
if(localRefCls == NULL){
return; //exeption
}
//创建全局引用
strGloClass = (jclass) (*env).NewGlobalRef(localRefCls);
//当本地引用不用的时候,释放掉
(*env).DeleteLocalRef(localRefCls);
//判断全局引用是否创建成功
if(strGloClass == NULL){
return; //out of memory exception
}
}
};
全局引用的使用及释放
JNIEXPORT jstring JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_useGlobalRef
(JNIEnv *env, jobject jobj, jcharArray chars){
//使用全局引用
jstring result = (jstring) (*env).NewObject(strGloClass, strInitID, chars);
//释放全局引用(这个根据自己需要释放,释放以后不能再使用全局引用)
(*env).DeleteGlobalRef(strGloClass);
return result;
};
java中使用
Log.i(TAG, "初始化全局引用");
jd.createGlobalRef();
Log.i(TAG, "传入:{'a', 'b', 'c'}");
Log.i(TAG, "使用全局引用,并释放全局引用");
result = jd.useGlobalRef(chars);
Log.i(TAG, "输出:" + result);
// result = jd.useGlobalRef(chars);
Log.i(TAG, "------------------------------------------");
输出结果:
- :: -/com.test.git.jnidemo I/MainActivity-: 初始化全局引用
- :: -/com.test.git.jnidemo I/MainActivity-: 传入:{'a', 'b', 'c'}
- :: -/com.test.git.jnidemo I/MainActivity-: 使用全局引用,并释放全局引用
- :: -/com.test.git.jnidemo I/MainActivity-: 输出:abc
- :: -/com.test.git.jnidemo I/MainActivity-: ------------------------------------------
tips
上面调用如果改成:
jd.createGlobalRef();
result = jd.useGlobalRef(chars);
Log.i(TAG, "输出:" + result);
result = jd.useGlobalRef(chars);
则会报错,因为全局变量被释放后,不能再被使用。
弱引用
弱引用使用NewGlobalWeakRef创建,使用DeleteGlobalWeakRef释放。
与全局引用类似,弱引用可以跨方法、线程使用。
与全局引用不同的是,弱引用不会阻止GC回收它所指向的VM内部对象。
当本地代码中缓存的引用不一定要阻止GC回收它所指向的对象时,弱引用就说一个最好的选择。
对于弱引用,我们使用时需要攀登缓存过的弱引用是指向活动类的对象,还是被GC回收。
引用比较
给定两个引用,可以使用IsSameObject来判断它们两个是否指向相同的对象。
比如:
(*env).IsSameObject(obj1, obj2)
如果指向相同对象,则返回JNI_TRUE(1)否则返回JNI_FALSE(0)。
JNI中的一个引用NULL指向JVM中的null对象。如果obj是一个局部或者全局引用,可以使用
(*env).IsSameObject(obj, NULL)
或者obj == NULL来判断obj是否指向一个null对象
在这一点上,弱引用有些不同。
一个NULL弱引用同样指向一个JVM中的null对象。但不同的是,在一个弱引用上面使用IsSameObject时,返回值的意义时不同的。
比如:
(*env).IsSameObject(wobj, NULL);
如果wobj已经被回收,会返回JNI_TRUE,如果wobj仍然指向一个活动对象,会返回JNI_FALSE。