JNI中的引用

前几天在写NDK项目的时候,了解到了关于JNI引用的一些概念,由于之前对于NDK的概念比较模糊,所以在这里记录一下对应的学习资料。

注:所有的资料可以再oracle官方文档介绍上看到。


在NDK引用中,总共有三个引用的方式:

  • 全局引用(Global Reference)
  • 局部引用(Local Reference)
  • 弱全局引用(Weak Global Reference)

上述的三个引用全都是引用了Java层的Object对象,也就是说在如果操作不当,会导致GC操作不能及时回收对应对象,导致内存泄漏的问题。

全局引用

首先了解一下全局引用,在jni.h中可以看到,全局引用的方式通过

jobject NewGlobalRef(jobject obj)
    { return functions->NewGlobalRef(this, obj); }

就可以获得一个全局的引用,这里查询一下oracle的官方资料,资料解释如下:

Creates a new global reference to the object referred to by the obj argument. The obj argument may be a global or local reference. Global references must be explicitly disposed of by calling DeleteGlobalRef().

意思说的很明确:通过此方法可以创建一个对于object(即传进来的第二个参数)的全局引用,无论object是全局变量的还是局部变量。生成全局引用后必须显式的调用DeleteGlobalRef()去删除对应的全局引用(在我们不需要使用的时候)。

删除全局引用方法如下:

void DeleteGlobalRef(jobject globalRef)
    { functions->DeleteGlobalRef(this, globalRef); }


Demo代码示例如下:


static jobject globalRef;
//设置全局引用
void setGlobal(JNIEnv *env, jobject obj){
    jclass contextClass= env->FindClass("android.content.Context");
    globalRef =(env->NewGlobalRef(contextClass));
}
//删除全局引用
void deteleGlobal(JNIEnv *env){
    if (globalRef != NULL) {
        env->DeleteGlobalRef(globalRef);
    }
}
局部引用

局部引用在jni.h的实现如下:

jobject NewLocalRef(jobject ref)
    { return functions->NewLocalRef(this, ref); }
	
 void DeleteLocalRef(jobject localRef)
    { functions->DeleteLocalRef(this, localRef); }

官方资料上的解释如下:

Local references are valid for the duration of a native method call. They are freed automatically after the native method returns. Each local reference costs some amount of Java Virtual Machine resource. Programmers need to make sure that native methods do not excessively allocate local references. Although local references are automatically freed after the native method returns to Java, excessive allocation of local references may cause the VM to run out of memory during the execution of a native method.

局部变量只在执行一个native方法内有效,当native方法执行完毕后,系统会自动的把分配的内存给释放掉。每个声明的局部变量都要消耗JVM的内存资源(这个应该是在JVM的本地方法栈以及堆上进行分配相关的资源)。开发需要确保不能再native方法中去过分的分配局部引用。即使局部引用能够在方法执行完毕主动释放,过分的分配局部引用可能会造成VM的oom的问题。

Demo示例代码如下:


void useLocal(JNIEnv *env, jobject obj){
    jclass contextClass= env->FindClass("android.content.Context");
    jobject localRef =(env->NewLocalRef(contextClass));
    ....//do sth
    env->DeleteLocalRef(localRef);
}

对于jni.h中的方法,当方法的返回为jobject对象,我们就需要注意是局部引用,为了以防万一,可以手动进行局部引用的释放,如GetObjectArrayElement()FindClass(),NewOject()等等;当然对于如果是一些使用New...或者Get....的方法的,也需要调用相对应的Release...()方法进行释放引用。

这里引申出一个小问题,如果我们在一个方法中的局部引用对象数量必须超过最大的数量,那该怎么办呢? 其实也好解决,oracle提供了对应的方法允许我们通过EnsureLocalCapacity ()进行扩容的操作。

方法实现如下:


jint EnsureLocalCapacity(JNIEnv *env, jint capacity);

通过当前方法,我们可以确定是否能在当前线程下创建对应capacity的数量的局部引用,返回0代表成功,否则返回负数或者报OOM异常。在默认的情况下,VM会确保每个方法至少16个数量的局部引用能被创建。

 if (env->EnsureLocalCapacity(len) != 0) {
        throw " EnsureLocalCapacity error";
        return;
    }
    for (int i = 0; i < len; i++) {
        jstring jstr = static_cast<jstring>(env->GetObjectArrayElement(arr, i));

     //不需要手动调用DeleteLocalRef

    }

除了上面的方法外,jni还提供了方法PushLocalFrame()PopLocalFrame ()来管理循环创建局部引用的操作。

//PushLocalFrame 
jint PushLocalFrame(JNIEnv *env, jint capacity);

//PopLocalFrame 
jobject PopLocalFrame(JNIEnv *env, jobject result);
  • PushLocalFrame 方法会创建一个局部栈帧来管理嵌套作用域的局部引用,参数二传入对应栈帧的大小。
  • PopLocalFrame 方法移除所有存储在栈帧的局部变量,消除对应的引用,如果result==NULL,则不返回东西,如果result不为NULL,返回对应object的引用。
//#define N_REFS ... /*最大局部引用数量*/
jstring other_jstr;
   if (env->PushLocalFrame(N_REFS) != 0) {
        ... /*内存溢出*/
    }
for (i = 0; i < len; i++) {
     jstring jstr = (*env)->GetObjectArrayElement(env, arr, i);
     ... /* 使用jstr */
     if (i == 2) {
        other_jstr = jstr;
     }
}
 other_jstr = env->PopLocalFrame(other_jstr);  // 销毁局部引用栈前返回指定的引用

在管理局部引用的生命周期中,Push/PopLocalFrame是非常方便的。你可以在本地函数的入口处调用PushLocalFrame,然后在出口处调用PopLocalFrame,这样的话,在函数对中间任何位置创建的局部引用都会被释放。

如果你在函数的入口处调用了PushLocalFrame,记住在所有的出口(有return出现的地方)调用PopLocalFrame。

大量的局部引用创建会浪费不必要的内存。一个局部引用会导致它本身和它所指向的对象都得不到回收。尤其要注意那些长时间运行的方法、创建局部引用的循环和工具函数,充分得利用Pus/PopLocalFrame来高效地管理局部引用。

弱全局引用

关于弱引用在jni.h的声明如下:

    jweak NewWeakGlobalRef(jobject obj)
    { return functions->NewWeakGlobalRef(this, obj); }

    void DeleteWeakGlobalRef(jweak obj)
    { functions->DeleteWeakGlobalRef(this, obj); }


官方资料的解释如下:

Weak global references are a special kind of global reference. Unlike normal global references, a weak global reference allows the underlying Java object to be garbage collected. Weak global references may be used in any situation where global or local references are used. When the garbage collector runs, it frees the underlying object if the object is only referred to by weak references. A weak global reference pointing to a freed object is functionally equivalent to NULL. Programmers can detect whether a weak global reference points to a freed object by using IsSameObject to compare the weak reference against NULL. Weak global references in JNI are a simplified version of the Java Weak References, available as part of the Java 2 Platform API ( java.lang.ref package and its classes).

弱全局引用类似于在Java层面的弱引用类型,允许Java对象被GC。如果Java层的对象的只被Native的变量弱全局引用了,那么当GC开始的时候,Java对象是可以被回收的,当Java对象被回收之后,Native的对应对象则等于NULL。开发同学可以使用IsSameObject()来判断当前弱全局引用对象是否被回收。

Demo代码示例如下:


static jobject globalRef;

void useLocal(JNIEnv *env, jobject obj){
    jclass contextClass= env->FindClass("android.content.Context");
    globalRef =(env->NewWeakGlobalRef(contextClass));
    ...
   
}
void deteleGlobal(JNIEnv *env){
    if (globalRef != NULL) {
        env->DeleteWeakGlobalRef(globalRef);
    }
}

参考资料:

https://blog.csdn.net/xyang81/article/details/44657385

https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html

转载于:https://my.oschina.net/u/3863980/blog/1840403

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值