通过 JNI 在 C 和 Java 之间传递指针

2022-08-31 13:01:55

目前,我正在尝试创建一个使用CUDA功能的Java应用程序。CUDA和Java之间的连接工作正常,但我还有另一个问题,想问一下,我的想法是否正确。

当我从Java调用一个本机函数时,我会向它传递一些数据,这些函数会计算一些东西并返回一个结果。有没有可能,让第一个函数返回一个引用(指针)到这个结果,我可以传递给JNI并调用另一个函数来进一步计算结果?

我的想法是减少将数据复制到GPU或从GPU复制数据所带来的开销,方法是将数据保留在GPU内存中,然后只传递对它的引用,以便其他函数可以使用它。

经过一段时间的尝试,我自己认为,这应该是不可能的,因为在应用程序结束后(在这种情况下,当C函数终止时),指针会被删除。这是正确的吗?还是我只是在C中不好才能看到解决方案?

编辑:好吧,稍微扩展一下问题(或者更清楚地说明):当函数结束时,JNI本机函数分配的内存是否被释放?或者,在JNI应用程序结束或手动释放它之前,我仍然可以访问它吗?

感谢您的输入:)


答案 1

我使用了以下方法:

在 JNI 代码中,创建一个结构,用于保存对所需对象的引用。首次创建此结构时,请将其指向 java 的指针作为 返回。然后,从java中,您只需调用任何具有此参数的方法,然后在C中将其转换为指向结构的指针。longlong

该结构将位于堆中,因此不会在不同的 JNI 调用之间清除它。

编辑:我不认为你可以使用长ptr =,因为地址是一个静态变量。按照Gunslinger47建议的方式使用它,即创建新的类或结构实例(使用new或malloc)并传递其指针。(long)&address;


答案 2

在C++您可以使用任何想要分配/释放内存的机制:堆栈,malloc/free,new/delete或任何其他自定义实现。唯一的要求是,如果您使用一种机制分配了内存块,则必须使用相同的机制释放它,因此您无法调用堆栈变量,也无法调用ed内存。freedeletemalloc

JNI 有自己的机制来分配/释放 JVM 内存:

  • NewObject/DeleteLocalRef
  • NewGlobalRef/DeleteGlobalRef
  • NewWeakGlobalRef/DeleteWeakGlobalRef

这些都遵循相同的规则,唯一的问题是,当本机方法退出时,可以显式地、使用 或隐式地“集体”删除本地引用。PopLocalFrame

JNI 不知道您如何分配内存,因此当您的函数退出时,它无法释放内存。堆栈变量显然会被销毁,因为您仍在编写C++,但您的 GPU 内存将保持有效。

唯一的问题是如何在后续调用时访问内存,然后您可以使用Gunslinger47的建议:

JNIEXPORT jlong JNICALL Java_MyJavaClass_Function1() {
    MyClass* pObject = new MyClass(...);
    return (long)pObject;
}

JNIEXPORT void JNICALL Java_MyJavaClass_Function2(jlong lp) {
    MyClass* pObject = (MyClass*)lp;
    ...
}