我需要在使用 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;
}
(我投票重新讨论这个问题,因为链接的重复项没有显示对 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
或在您忘记时自动调用它。