JNI 中已弃用的 Finalize 方法的替代方法

问题描述 投票:0回答:1

我需要在使用 JNI 的地方实现 C++ 和 Java 之间的双向通信。大多数问题都是通过使用 JNI 解决的,但我更担心的是,我将 C++ 堆内存中变量的指针引用传递给 Java。我使用

new
关键字在堆内存中分配引用,并将其传递给 Java 以便将来引用该特定变量。因此,我必须使用
delete
从 C++ 的堆内存中释放变量。通常在 Java 中,这是通过使用重写
finalize()
方法来实现的,因此垃圾收集器在删除对象时会调用此方法,但我发现
finalize
从 Java 9 开始已被弃用。还有其他方法来实现所需的功能吗?

C++ 代码(将 C++ 对象引用传递给 Java):

jobject Helper::toJavaHistory(const History& history) {

    auto* historyPtr = new History(history);
    jobject historyObject = getEnv()->NewObject(getHistoryClass(), getMethodID("History","<init>"),reinterpret_cast<jlong>(historyPtr));
    return historyObject;
}

Java代码:

public class History {
    
    public long historyPointer;

    public History(long historyPointer){
        this.historyPointer = historyPointer;
    }
    native void deleteHistory(long historyPtr);

}

JNI 本机实现 C++:

JNIEXPORT void JNICALL Java_com_example_core_History_deleteHistory
        (JNIEnv * env, jobject thisHistoryObject, jlong historyPtr) {
    auto* history = reinterpret_cast<History*>(historyPtr);
    delete history;
}
java c++ performance memory-management java-native-interface
1个回答
0
投票

(我投票重新讨论这个问题,因为链接的重复项没有显示对 JNI 代码执行此操作的方法)

Java 9 中引入的 Cleaner API 允许您在对象变得无法访问并因此符合垃圾回收条件时注册

Runnable
对象。

我将展示两种涉及 JNI 方法的等效方法。两者都要求您将

Cleaner
与您的历史课程相关联:

static Cleaner cleaner = cleaner.create();

对于您来说,最终目标是在保存变量状态的

static
上调用
deleteHistory
(!)
long
方法。

漫长的道路

既然

Cleaner
想要一个
Runnable
,让我们定义一个:

static class HistoryCleaner implements Runnable {
  private long ptr;
  public HistoryCleaner(long ptr) { this.ptr = ptr; }
  public void run() {
    History.deleteHistory(this.ptr);
  }
}

确保

HistoryCleaner
不会创建对
Cleaner
对象的任何引用,因为这会导致引用循环并阻止该对象被垃圾收集。

对于更高级的用例,您可以将

run
方法定义为
native
并在 C++ 中将其实现为

JNIEXPORT void JNICALL Java_HistoryCleaner_run(JNIEnv *env, jobject thiz) { ... }

请记住,

thiz
对象是您的
HistoryCleaner
,而不是
History
对象。 构造函数现在看起来像这样:

public History(long historyPointer){
    this.historyPointer = historyPointer;
    this.cleanable = cleaner.register(this, new HistoryCleaner(historyPointer));
}

短手

您可以使用 lambda 语法来跳过创建

Runnable
,但您必须小心引用有关该对象的任何内容。因此,构造函数可以变成:

public History(long historyPtr){
    this.historyPointer = historyPtr;
    this.cleanable = cleaner.register(this, () -> History.deleteHistory(historyPtr));
    // Note: historyPtr is a local variable so this does not keep a reference to the object.
}

关于(自动)可关闭的注释

如 Cleaner 文档所示,由

cleanable
返回的
Cleaner#register
对象对于实现
Closeable#close
方法很有用:

public void close() {
   cleanable.clean();
}

这允许您使用 java try-with-resources 形式隐式调用

deleteHistory
或在您忘记时自动调用它。

© www.soinside.com 2019 - 2024. All rights reserved.