【问题标题】:JNI: Invoke JAVA method from C++, returned objects, References and GCJNI:从 C++ 调用 JAVA 方法、返回的对象、引用和 GC
【发布时间】:2015-07-12 16:47:16
【问题描述】:

当 C++ 程序(!)调用返回对象的 java 方法时,规则是什么?是否有对该对象的出色引用? 我必须调用“DeleteLocalRef”来确保对象是 GCd,还是会自动 GCd?这对我来说不清楚的原因是因为在所有示例和官方文档中都声明“DeleteLocalRef”不是必需的,只有当从 java 调用本机“C/C++”方法时才有意义。但是如果调用线程是调用java的C++方法,JVM怎么知道对象可以GCd呢?

同理:如果我想缓存对象,我必须调用“NewGlobalRef”吗?

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

感谢任何参考和/或澄清

【问题讨论】:

    标签: java c++ object reference java-native-interface


    【解决方案1】:

    其实解释的比较详细here

    全球和本地参考

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

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

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

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

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

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

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

    【讨论】:

    • 谢谢!我看到了这个,但这个解释似乎与从 java 调用的 Native (C++) 有关。它似乎与调用返回对象的java方法的C++程序无关......在C++调用java的情况下,没有:“......在本机方法返回后”
    • 那么您是说您的 C++ 程序托管/创建了 jvm?
    • 在这种情况下,我很确定您应该致电DeleteLocalRef(如果合适),因为您必须自己控制生命周期。
    【解决方案2】:

    我认为j.holetzeck 的回答遗漏了重要部分。

    来自同一个documentation

    实现本地引用

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

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

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

    1. Android Activity 以 Java 启动。
    2. Android Activity 每帧调用一次 C++ 来运行我们应用的更新循环。
    3. 在此 C++ 更新循环期间,我们的 C++ 代码使用 JNI 对 Java 进行任意数量的调用。

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

    如果您的应用程序在 C++ 中开始执行然后调用 Java,我认为您永远不会有自动本地引用垃圾收集,除非您再次从 Java 调用 C++(调用堆栈:C++->Java->C++),其中万一当您返回 Java (C++->Java->C++ -> C++->Java) 时,您在最顶层的 C++ 函数中所做的任何引用都将被 GC 处理。

    【讨论】:

    • 这是否意味着所有传入JNI的对象都是unmovable直到JNI返回?我可以绕过 JNI 直接从内存中读取,而无需对象从一条指令移动到另一条指令吗?
    • @JohnnyV 我不太确定,但文档确实说A registry maps nonmovable local references to Java objects。因此,听起来至少会有 references 保证不会移动,直到删除 registry(在从 C++ 回到 Java 的 transition of control 上)。我不能说实际数据本身是否会一直停留在同一个位置(我不熟悉 Java 的内存管理方案的细节)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-28
    相关资源
    最近更新 更多