JNI中的引用
文章目录
前言
平时写 Java
代码对创建的对象,我们很少会去关心怎么手动释放内存,大多数时候都有 GC
去帮我们回收。
然而在 JNI
中,几乎都是 C/C++
的代码,它们是没有 GC
的,所以对内存的使用就需要格外小心了。
因此在 JNI
中提供了三种引用类型,分别是局部引用、全局引用、弱全局引用。下面介绍这三种引用。
局部引用
我们平时和局部引用打交道是最多的,比如我们经常在函数中使用的 NewStringUTF("hello,world")
,这就是一个局部引用。
局部引用只有在函数的作用域内才有效,函数执行完成之后会自动释放,所以即便没有手动释放也没有关系。
但是创建过多局部引用就有可能导致内存溢出或引用表溢出,这种情况大多出现在有循环的代码中。所以最好我们在使用完之后主动调用 DeleteLocalRef
来手动释放。
PushLocalFrame & PopLocalFrame
有时候我们需要创建许多对象,但是一个一个的手动释放又非常麻烦,为了解决这个问题 JNI
提供了 PushLocalFrame
与 PopLocalFrame
来解决这个问题。
它们是配套使用的,需要成对出现。如下所示
|
|
在 PushLocalFrame
之后的代码会在遇到 PopLocalFrame
后一次性全部释放,这样我们就不用手动一个一个的去释放了。
上面这种情况是没有返回值的,如果有返回局部栈帧中的局部引用应该怎么办?
JNI
提供的 PopLocalFrame
其实是有一个参数的, jobject PopLocalFrame(jobject result);
我们把需要保留的局部引用放到 result
中就可以了,上面的例子中我们传入 NULL
表示不需要返回到之前的栈帧中。
|
|
这样 jstr1
就会在父帧中被重新创建,所以其实 jstr1
还是会失效。只不过重新创建了一个新的引用。
EnsureLocalCapacity
在 JNI
规范中指出, JVM
要确保每个 Native
方法至少可以创建 16 个局部引用,所以我们平时开发一般是够用的,但是就是会碰到极个别情况下超过 16 的,这时候就需要使用 EnsureLocalCapacity
来确保是否能够创建足够的局部引用。
|
|
EnsureLocalCapacity
返回 0
表示创建成功。
全局引用
上一节我们说道局部引用在函数退出后会被释放,并且局部变量不能跨函数,线程使用。
但我们有时候需要把局部变量保存起来,以便在其它方法或线程中使用。这时候我们会想到使用静态变量来进行赋值,但是这样做法是有问题的。
因为即便你赋值给静态变量,它本质上还是局部引用,函数结束后还是会被释放,所以在函数结束后静态变量其实指向了一个非法地址。
为了解决这个问题, JNI
提供了 NewGlobalRef
把局部引用转为全局引用。
|
|
这里需要注意判断两个 Java
对象是不是同一个,需要用 IsSameObject
,而不是用 ==
判断。
|
|
在不使用的时候记得要使用 env->DeleteGlobalRef(callback)
释放。
弱全局引用
弱全局引用和全局引用一样也是全局引用,但是弱全局引用并不会阻止对象被回收。
弱全局引用使用 env->NewWeakGlobalRef(callback)
进行初始化,使用 env->DeleteWeakGlobalRef
来删除全局弱引用。
|
|