天天看点

Android NDK开发系列教程5:局部引用,全局引用,弱全局引用

从Java虚拟机创建的对象当传入到native层时会产生一个引用,在进行垃圾回收时如果有native的引用,改对象同样也不会被回收。在native引用中分局部引用和全局引用。

局部引用又称本地引用,大多数见到的引用都是局部引用,例如通过NewLocalRef和各种JNI接口创建(FindClass、NewObject、GetObjectClass和NewCharArray等),局部引用只会在本次native调用中有效,当本次调用结束后该引用即被自动释放。局部引用会阻止GC进行回收。同时也可以调用DeleteLocalRef函数来手动释放(比如在循环里面用到了局部引用而退出循环没有使用该局部引用,那么就需要在循环中释放该局部引用)。通常使用NewObject创建的实例返回的也是局部引用。千万不要把局部引用保存为c++的全局变量或者把它定义为静态变量,局部引用的有效期是一次Java本地调用。

JNI提供了一系列函数来管理局部引用的生命周期。这些函数包括:EnsureLocalCapacity、NewLocalRef、PushLocalFrame、PopLocalFrame、DeleteLocalRef。

PushLocalFrame为当前函数中局部引用创建了一个引用堆栈,在每遍历一次调用(*env)->GetObjectArrayElement(env, arr, i);返回一个局部引用时,JVM会自动将该引用压入当前局部引用栈中。而PopLocalFrame负责将栈中所有引用释放。这样一来,Push/PopLocalFrame函数对提供了对局部引用生命周期更方便的管理,不用再去一个个Delete了。

全局引用可以在当前线程使用,也可以在其他线程使用,可以保存在本地的static静态变量或全局变量中,全局引用需要调用NewGlobalRel函数创建,释放时采用ReleaseGlobalRef函数释放。有效作用域在创建后,一直到调用ReleaseGlobalRef释放时。

在Java1.2中,新增了弱全局引用,与全局变量一样其创建、删除均需要编程写出,也可以在本地多个代码中使用,也可以跨进程使用。不一样的是,它的存在不影响垃圾回收机制对该引用所指向对象实例的回收。其创建采用NewWeakGlobalRef,释放采用ReleaseWeakGlobalRel。

以上涉及的函数主要有以下几个:

上述三中引用会影响内存的回收,在C/C++中没有向Java一样的垃圾回收机制,自己申请的内存要记得自己去释放了,否则会导致内存泄漏。虽然现在C/C++里面也有智能指针,但相对而言这个智能指针用起来不如Java。所以在C的世界里要遵循谁申请,谁释放的基本原则。

上面介绍了基本知识,下面给出相应的例子来进行说明下。

在局部引用中要注意以下几方面:

1. 循环体内创建的局部引用,要在循环体内就直接释放了。

2. 编写的工具函数,里面创建的局部引用,要在该工具函数里面释放了。

3. 局部引用引用了一个大的Java对象,这时候一定一定要早点释放了。

4. 局部引用不要缓存在native层

弱全局引用和全局引用基本差不多,最大的区别就是弱全局引用不影响GC的回收。在使用弱全局引用的时候一定要注意,使用前要检查下是不是被GC回收了。

jni提供了相应的函数

如果两个引用指向同一个实例则返回JNI_TRUE,否则返回JNI_FALSE。