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

目前,我正在尝试创build一个使用CUDAfunction的Java应用程序。 CUDA和Java之间的连接工作正常,但我有另一个问题,并想问,如果我的想法是正确的。

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

我的想法是通过将数据保留在GPU内存中,并将参考传递给GPU,从而减less将数据复制到GPU以及从GPU复制数据的开销,以便其他function可以使用它。

尝试一段时间后,我想我自己,这应该是不可能的,因为应用程序结束后(在这种情况下,当C函数终止)指针被删除。 它是否正确? 或者我只是在C坏了看到解决scheme?

编辑:好,扩大一点问题(或者更清楚地说):当函数结束时,由JNI原生函数分配的内存是否被释放? 或者我仍然可以访问它,直到JNI应用程序结束或当我手动释放它?

感谢您的input :)

我使用了以下方法:

在你的JNI代码中,创build一个结构来保存对你需要的对象的引用。 当你第一次创build这个结构体时,返回它作为一个long指针。 然后,从java中你只要调用任何一个参数的方法,并在C中将其转换为指向你的结构的指针。

结构将在堆中,所以不会在不同的JNI调用之间清除。

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

在C ++中,你可以使用任何你想要分配/释放内存的机制:堆栈,malloc / free,new / delete或者其他自定义的实现。 唯一的要求是,如果你用一种机制分配了一块内存,你必须用相同的机制释放内存,所以你不能在堆栈variables上调用free ,也不能在malloc内存上调用delete

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

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

这些遵循相同的规则,唯一的问题是本地引用可以显式地使用PopLocalFrame来“删除”,或者在本地方法退出时隐式删除​​。

JNI不知道如何分配你的内存,所以当你的函数退出的时候它不能释放它。 堆栈variables显然会被破坏,因为你仍然在编写C ++,但你的GPU内存将保持有效。

唯一的问题是如何在随后的调用中访问内存,然后你可以使用Gunslinger47的build议:

 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; ... } 

Java不会知道如何处理指针,但它应该能够存储来自本地函数的返回值的指针,然后将其交给另一个本地函数来处理。 C指针只不过是核心的数值。

另一个contibutor将不得不告诉你指向的graphics内存是否会在JNI调用之间被清除,以及是否有任何解决方法。

如果您在本机函数内部dynamic分配内存(在堆上),则不会被删除。 换句话说,您可以使用指针,静态variables等,将不同调用之间的状态保持为本地函数。

想想它的另一种方式:你可以安全地保存在另一个C ++程序调用的函数调用中吗? 这里同样适用。 当一个函数退出时,该函数调用栈上的任何内容都被销毁; 但堆中的任何内容都将保留,除非您明确删除它。

简单的回答:只要你不释放你返回到调用函数的结果,它将在稍后重新入口时保持有效。 只要确保清理完成。

我知道这个问题已经正式回答了,但我想添加我的解决scheme:而不是试图传递一个指针,将指针放在一个Java数组(索引0),并将其传递给JNI。 JNI代码可以使用GetIntArrayRegion / SetIntArrayRegion获取和设置数组元素。

在我的代码中,我需要本地层来pipe理文件描述符(一个打开的套接字)。 Java类包含一个int[1]数组,并将其传递给本机函数。 本地函数可以做任何事情(get / set)并将结果放回数组中。

虽然从@ denis-tulskiy接受的答案确实是有道理的,但我已经亲自按照这里的build议。

因此,不要使用诸如jlong这样的伪指针types(或者如果要在32位拱门上保存一些空间,则使用jint ),而是使用ByteBuffer 。 例如:

 MyNativeStruct* data; // Initialized elsewhere. jobject bb = (*env)->NewDirectByteBuffer(env, (void*) data, sizeof(MyNativeStruct)); 

您可以稍后重新使用它:

 jobject bb; // Initialized elsewhere. MyNativeStruct* data = (MyNativeStruct*) (*env)->GetDirectBufferAddress(env, bb); 

对于非常简单的情况,此解决scheme非常易于使用。 假设你有:

 struct { int exampleInt; short exampleShort; } MyNativeStruct; 

在Java方面,你只需要做:

 public int getExampleInt() { return bb.getInt(0); } public short getExampleShort() { return bb.getShort(4); } 

这可以节省你写大量的样板代码! 但是,应该注意这里所说的字节顺序。

最好做到这一点是不安全的.allocateMemory是如何做的。

创build你的对象然后键入它(uintptr_t)这是一个32/64位无符号整数。

 return (uintptr_t) malloc(50); void * f = (uintptr_t) jlong; 

这是做这件事唯一正确的方法。

这里是检查Unsafe.allocateMemory的完整性。

 inline jlong addr_to_java(void* p) { assert(p == (void*)(uintptr_t)p, "must not be odd high bits"); return (uintptr_t)p; } UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory(JNIEnv *env, jobject unsafe, jlong size)) UnsafeWrapper("Unsafe_AllocateMemory"); size_t sz = (size_t)size; if (sz != (julong)size || size < 0) { THROW_0(vmSymbols::java_lang_IllegalArgumentException()); } if (sz == 0) { return 0; } sz = round_to(sz, HeapWordSize); void* x = os::malloc(sz, mtInternal); if (x == NULL) { THROW_0(vmSymbols::java_lang_OutOfMemoryError()); } //Copy::fill_to_words((HeapWord*)x, sz / HeapWordSize); return addr_to_java(x); UNSAFE_END