引用的释放管理

释放引用

每一个JNI引用被建立时,除了它所指向的JVM中的对象外,引用本身也会消耗掉一定数量的内存。

释放局部引用

大部分情况下,实现本地方法时不必担心局部引用的释放问题,因为本地方法被调用完成后,JVM会自动回收这些局部引用。
尽管如此,以下几种情况,为了避免内存溢出,应该手动释放局部引用:
1.在实现一个本地方法调用时,你需要创建大量的局部引用。这种情况啃根会导致JNI局部引用表的溢出,所以最好在局部引用不需要时立即手动删除。
比如:

for(i = 0; i < len; i++){
    jstring jstr = (*env).GetObjectArrayElement(arr, i);
    ....
    (*env).DeleteLocalRef(jstr);
}

2.你想写一个工具函数,这个函数被谁调用是不知道的。
比如在工具函数类使用引用后,使用DeleteLocalRef删除。
3.你的本地方法不会返回任何东西。例如:一个本地方法可能会在一个事件接收循环里面被调用,这种情况下,为了不让局部引用累积造成内存溢出,手动释放也是必须的。
4.你的本地方法访问一个大对象,因此创建了一个对这个大对象的引用。然后本地方法在返回前会有一个做大量的计算过程,而在这个过程中时不需要前面创建的对大对象的引用的。但是,在计算过程,对大对象的引用会阻止GC回收大对象。

管理局部引用

JDK 提供了一系列的函数来管理局部引用的生命周期。这些函数包括: EnsureLocalCapacity、NewLocalRef、PushLocalFrame、PopLocalFrame。
JNI 规范中指出,VM 会确保每个 地方法可以创建至少 16 个局部引用。经验表 明,这个数量已经满足大多数不需要和 JVM 中的内部对象有太多交互的 地方法。 如果真的需要创建更多的引用, 地方法可以通过调用 EnsureLocalCapacity 来支持更多的局部引用。

if((*env).EnsureLocalCapacity(len) < 0){
    //out of memory
}

PushLocalFrame 为一定数量的局部引用创建了一个使用堆栈,而 PopLocalFrame 负责销毁堆栈顶端的引用。
Push/PopLocalFrame 函数对提供了对局部引用的生命周期更方便的管理。上面 的例子中,如果处理 jstr 的过程中创建了局部引用,则 PopLocalFrame 执行时, 这些局部引用全部会被销毁。

for(i = 0; i < len; i++){
    if((*env).PushLocalFrame(N_REFS) < 0){
        //out of memory
    }
    jstr = (*env).GetObjectArrayElement(arr, i);
    (*env).PopLocalFrame(NULL);
}

当你写一个会返回局部引用的工具函数时,NewLocalRef 非常有用。

地代码可能会创建大量的局部引用,其数量可能会超过 16 个或 PushLocaFrame
和 EnsureLocalCapacity 调用设置的个数。VM 可能会尝试分配足够的内 存,但不能够保证分配成功。如果失败,VM 会退出。

释放全局引用

当你的 地代码不再需要一个全局引用时,你应该调用 DeleteGlobalRef 来释放 它。如果你没有调用这个函数,即使这个对象已经没用了,JVM 也不会 回收这个全局引用所指向的对象。
当你的 地代码不再需要一个弱引用时,应该调用 DeleteWeakGlobalRef 来释放 它,如果你没有调用这个函数,JVM 仍会回收弱引用所指向的对象,但 弱引用 身在引用表中所占的内存永远也不会被回收。

管理引用的规则

JNI 引用的管理规则了, 目标就是减少内存使用和对象被引用保持而不能释放。

通常情况下,有两种 地代码:直接实现 地方法的 地代码和可以被使用在任 何环境下的工具函数。
1.当编写实现 地方法的 地代码时,当心不要造成全局引用和弱引用的累加,因 为 地方法执行完毕后,这两种引用不会被自动释放。
2.当编写一个工具函数的 地代码时,当心不要在函数的调用轨迹上面遗漏任何的 局部引用,因为工具函数被调用的场合是不确定的,一旦被大量调用, 很有可能造成内存溢出。

编写工具函数时,应遵守的规则

1.一个返回值为基本类型的工具函数被调用时,它绝不能造成局部、全局、弱引用不被回收的累加。
2.当一个返回值为引用类型的工具函数被调用时,它除了返回的引用以外,绝不能造成其它局部、全局、弱引用的累加。

如果一个工具函数返回一个引用,你应该详细说明返回的引用类型,以便调用者更好的管理它们。
例如:

while(JNI_TRUE){
    jstring infoString = GetInfoString(info);
    //我们需要知道infoString的引用类型,然后调用DeleteLcoalRef、DeleteGlobalRef、DeleteWeakGlobalRef。
}

函数NewLocalRef有时被用来确保一个工具函数返回一个局部引用

jstring MyNewString(JNIEnv *env){
    static jstring cachedString = NULL;
    if(cachedString == NULL){
        jclass stringClass = (*env).FindClass("java/lang/String");
        //创建cachedStringLocal
        jstring cachedStringLocal = (jstring) (*env).NewObject(stringClass, strInitID);
        //缓存全局引用
        cachedString = (jstring) (*env).NewGlobalRef(cachedStringLocal);

        (*env).DeleteLocalRef(cachedString);
    }
    //创建并且保存本地引用字符串
    return (jstring) (*env).NewLocalRef(cachedString);
}

Push/PopLocalFrame

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

如果你在函数的入口处调用了 PushLocalFrame,记住在所有的出口(有 return 出现的地方)调用 PopLocalFrame。在下面的代码中,对 PushLocalFrame 的调 用只有一次,但对 PopLocalFrame 的调用却需要多次。

jobject f(JNIEnv *env, ...){
    jobject result;
    if(*env)->PushLocalFrame(env, 10) < 0){
        return NULL;//frame not pushed, no PopLocalFrame needed
    }
    ...
    result=...;
    if(...){
        //在返回前不要忘了调用PopLocalFrame
        result= (*env).PopLocalFrame(result);
        return result;
    }
    ...
    result = (*env).PopLocalFrame(result);
    //正常返回
    return result;
}

上面的代码同样演示了函数 PopLocalFrame 的第二个参数的用法。局部引用 result 一开始在 PushLocalFrame 创建的当前 frame 里面被创建,而把 result 传入 PopLocalFrame 中时,PopLocalFrame 在弹出当前的 frame 前,会由 result 生成一个新的局部引用,再把这个新生成的局部引用存储在上一个 frame 当中。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值