JNI:从C++调用JAVA方法,返回对象,引用和GC

JNI: Invoke JAVA method from C++, returned objects, References and GC

本文关键字:返回 对象 GC 引用 方法 C++ 调用 JAVA JNI      更新时间:2023-10-16

当C++程序(!)调用返回对象的java方法时,规则是什么?是否有对该对象的突出引用?我必须调用"DeleteLocalRef"来确保对象是GCd吗?还是会自动为GCd?我不清楚的原因是,在所有的示例和官方文档中,"DeleteLocalRef"是不必要的,只有当从java调用本机"C/C++"方法时,它才对我有意义。但是,如果调用线程是一个调用java的C++方法,JVM将如何知道该对象可以是GCd?

同样:如果我想缓存对象,我必须调用"NewGlobalRef"吗?

找不到对此的任何引用。。。

感谢的任何参考和/或澄清

我认为j.holetzeck的回答忽略了重要部分。

来自同一文档:

实施本地参考

为了实现本地引用,Java VM为控制Java的每次转换创建一个注册表。注册表将不可移动的本地引用映射到Java对象,并防止对象被垃圾收集。传递给本机方法的所有Java对象(包括那些作为JNI函数调用结果返回的对象)都会自动添加到注册表中。在本机方法返回后,注册表将被删除,从而允许对其所有条目进行垃圾收集。

我将以我的应用程序为例解释我对此的解释:

我的应用程序流程的高级概述:

  1. Android活动在Java中启动
  2. 每帧一次,Android活动调用C++来运行我们应用程序的更新循环
  3. 在这个C++更新循环中,我们的C++代码使用JNI对Java进行任意数量的调用

我如何解释引用的文档是,自动垃圾收集可以而且只会在步骤2中发生,当我们的C++更新循环返回到最初调用它的Java代码时。

如果你的应用程序开始在C++中执行,然后调用Java,我认为你永远不会有自动的本地引用垃圾收集,除非你再次从Java调用C++(调用堆栈:C++->Java->C++),在这种情况下,当你返回Java时,你在最顶端的C++函数中所做的任何引用都将是GC’d(C++->Java->C++->C++->Java)。

实际上这里有一些详细的解释:

全球和本地参考

JNI将本机代码使用的对象引用分为两类:本地引用和全局引用。本地引用在本机方法调用期间有效,并且在本机法返回后自动释放。全局引用在显式释放之前保持有效。

对象作为本地引用传递给本机方法JNI函数返回的所有Java对象都是本地引用。JNI允许程序员从本地引用创建全局引用JNI函数期望Java对象同时接受全局和本地引用。本机方法可以返回对VM的本地或全局引用作为其结果。

在大多数情况下,程序员应该依靠VM在本机方法返回后释放所有本地引用。然而,有时程序员应该明确地释放本地引用。例如,考虑以下情况:

  • 本机方法访问大型Java对象,从而创建对Java对象的本地引用。然后,本机方法在返回到调用者之前执行额外的计算。对大型Java对象的本地引用将防止该对象被垃圾收集,即使该对象在剩余的计算中不再使用。

  • 本机方法创建大量的本地引用,尽管并非所有本地引用都同时使用。由于虚拟机需要一定的空间来跟踪本地引用,因此创建过多的本地引用可能会导致系统内存不足。例如,本机方法循环遍历一个大型对象数组,将元素检索为本地引用,并在每次迭代中对一个元素进行操作。在每次迭代之后,程序员不再需要对数组元素的本地引用。

JNI允许程序员在本机方法中的任何时候手动删除本地引用。为了确保程序员可以手动释放本地引用,不允许JNI函数创建额外的本地引用,除非它们作为结果返回引用。

本地引用仅在创建它们的线程中有效。本机代码不能将本地引用从一个线程传递到另一个线程。