Android jni/ndk编程四:jni引用类型

一.JNI引用类型

JNI支持三种类型的 opaque reference:local references, global references,和weak global references,下面我们将逐一探讨。

local references

大部分JNI 函数都会创建LocalRef,如NewObject创建一个实例,并返回一个指向该实例的LocalRef。LocalRef只在本线程的 native method中有效. 一但native method返回,LocalRef 将被释放。这意味着我们不能缓存LocalRef来提高效率,因为native方法一旦返回,LocalRef将被释放,缓存起来也没有用。 
我们已经知道了大部分JNI函数都会创建LocalRef,比如NewObject,FindClass等,与创建相对应的,我们可以有两种方式让LocalRef 无效:

  1. native method返回,JavaVM自动释放LocalRef;
  2. 用DeleteLocalRef 主动释放。 
    DeleteLocalRef的定义如下:
    void        (*DeleteLocalRef)(JNIEnv*, jobject);

DeleteLocalRef 主动释放LocalRef是有意义的,如果我们内存特别紧张,而一个本地的方法有很多很耗内存的LocalRef,这个时候,在本地方法中及时释放这些LocalRef可以缓解内存压力。 
LocalRef只在创建该对象的线程中有效,因此我们无法在其他线程中使用共享LocalRef.

Global References

Global引用类似于全局变量,我们可以在多个native方法中使用,也可以在多个线程中使用。此外,Golbal引用会阻止GC回收。但是,与c总的全局变量不同的是,Golbal引用必须通过特定的函数手动创建和释放。以下是相关函数的定义:

    jobject     (*NewGlobalRef)(JNIEnv*, jobject);
    void        (*DeleteGlobalRef)(JNIEnv*, jobject);

使用举例:

//1.首先定义一个静态变量存储Global引用
static jclass stringClass = NULL;
//2.获得Local 引用
jclass localRefCls = (*env)->FindClass(env,
"java/lang/String");
if (localRefCls == NULL) {
return NULL; /* exception thrown */
}
//3.使用那个local引用创建全局引用
stringClass = (*env)->NewGlobalRef(env, localRefCls);
//4.及时释放Local引用
(*env)->DeleteLocalRef(env, localRefCls);

以上是常见的、简单的创建Global引用的代码流程,使用完以后,记得把它释放掉,不然后内存泄漏了,因为Global引用是阻止GC的。

Weak Global References

所谓弱全局引用就是全局引用的另一个版本,既然它是全局引用,那么它就具备了在多个线程中共享的能力,以及我们可以在单个线程的不同函数中都可以使用它。之所以说它弱是因为它无法阻止GC。前面我们说Global引用很强势,它只能手动释放,JVM虚拟机不能自动GC它,但是Weak Global就会被垃圾回收器回收,这就是它若的原因。 
Weak Global Ref用 NewGlobalWeakRef于DeleteGlobalWeakRef进行创建和删除,多个本地方法调用过程中和多线程上下文中使用的特性与 GlobalRef相同。这两个函数在jni.h中的定义如下:

    jweak       (*NewWeakGlobalRef)(JNIEnv*, jobject);
    void        (*DeleteWeakGlobalRef)(JNIEnv*, jweak);

我们看到这里又出现了一个新的类型:jweak,它其实就是jobject,只是名字不容而已:

typedef jobject         jweak;

用法举例:

static jclass string= NULL;
if (string == NULL) {
    jclass local_string =
    (*env)->FindClass(env, "java/lang/String");
    if (myCls2Local == NULL) {
        return; /* can’t find class */
    }
    string = NewWeakGlobalRef(env, local_string );
    if (myCls2 == NULL) {
        return; /* out of memory */
    }
}

由于Weak Global引用可能被垃圾回收器回收,所以我们在使用它之前一定要判断它是否为空。如果空的话需要重新创建它,不为空就继续使用。

二.Comparing Reference

既然我们可以有多个引用,它可能是全局引用,弱全局引用或局部引用,我们怎么判断它是不是同一个引用呢?不用急,JNI已经为我们提供好了函数,我们可以直接用,其定义如下:

jboolean    (*IsSameObject)(JNIEnv*, jobject, jobject);

如果相通,返回JNI_TRUE, 否则返回JNI_FALSE。

三.引用管理

引用管理是为了较少内存使用,提高代码效率。JNI支持的三种引用各有各得用途,绝不能滥用。笔者水平有限,就不多废话了,这里主要讲一下Local引用的管理。 
JNI提供了一组管理Local引用的函数:

    jint        (*PushLocalFrame)(JNIEnv*, jint);
    jobject     (*PopLocalFrame)(JNIEnv*, jobject);

在进入本地方法时,调用一次 
PushLocalFrame,并在本地方法结束时调用 PopLocalFrame. 此对方法执行效率非常高,建议使用这对方法。 
一定保证该上下文出口只有一个,或每个return语句都做严格检查是否调用了PopLocalFrame。因为如果忘记调用PopLocalFrame 可能会使JVM崩溃。 
用法举例:

jobject hello(JNIEnv *env, jobject obj)
{
jobject result;
//进入函数后push
if ((*env)->PushLocalFrame(env, 10) < 0) {
/* frame not pushed, no PopLocalFrame needed */
return NULL;
}
...
result = ...;
if (...) {
//认真检查每一个函数的出口,绝不能忘记PopLocalFrame
result = (*env)->PopLocalFrame(env, result);
return result;
}
...
//正常返回前pop一下
result = (*env)->PopLocalFrame(env, result);
return result;
}
posted @ 2017-05-23 18:42  chenxibobo  阅读(577)  评论(0编辑  收藏  举报