目前,我正在尝试创建一个使用CUDA功能的Java应用程序。CUDA和Java之间的连接工作正常,但我有另一个问题,想问,如果我的想法是正确的。
当我从Java调用一个原生函数时,我向它传递一些数据,函数计算一些东西并返回一个结果。有没有可能让第一个函数返回一个指向这个结果的引用(指针),我可以把它传递给JNI,然后调用另一个函数来对这个结果做进一步的计算?
我的想法是通过将数据保留在GPU内存中并传递一个引用来减少从GPU复制数据的开销,以便其他函数可以使用它。
在尝试了一段时间后,我自己认为这是不可能的,因为指针在应用程序结束后被删除(在本例中,当C函数终止时)。是这样吗?或者我只是在C中看到解决方案太糟糕了?
编辑:好吧,把问题扩展一下(或者说得更清楚):JNI本机函数分配的内存是否在函数结束时释放?或者我仍然可以访问它,直到JNI应用程序结束或当我手动释放它?
感谢您的输入:)
8条答案
按热度按时间xpcnnkqh1#
我使用了以下方法:
在JNI代码中,创建一个结构体来保存对所需对象引用。当你第一次创建这个结构体时,把它的指针作为
long
返回给java。然后,在java中,你只需要调用任何带有long
作为参数的方法,然后在C中将其转换为指向你的结构体的指针。该结构将位于堆中,因此它不会在不同的JNI调用之间被清除。
编辑:我不认为你可以使用long ptr =
(long)&address;
,因为address是一个静态变量。使用Gunslinger47建议的方式,即。创建类或结构的新示例(使用new或malloc)并传递其指针。gjmwrych2#
在C++中,你可以使用任何你想要的机制来分配/释放内存:堆栈、malloc/free、new/delete或任何其他自定义实现。唯一的要求是,如果你用一种机制分配了一块内存,你必须用同样的机制释放它,所以你不能在堆栈变量上调用
free
,也不能在malloc
ed内存上调用delete
。JNI有自己的机制来分配/释放JVM内存:
它们遵循相同的规则,唯一的问题是本地引用可以被删除,无论是显式的,使用
PopLocalFrame
,还是隐式的,当本机方法退出时。JNI不知道你是如何分配内存的,所以当你的函数退出时,它不能释放内存。堆栈变量显然会被销毁,因为你仍然在写C++,但你的GPU内存仍然有效。
唯一的问题是如何在后续调用中访问内存,然后你可以使用Gunslinger 47的建议:
rt4zxlrg3#
虽然来自@denis-tulskiy的公认答案确实有道理,但我个人还是遵循了here的建议。
因此,不要使用伪指针类型,如
jlong
(如果你想在32位的arch上保存一些空间,也可以使用jint
),而是使用ByteBuffer
。举例来说:您可以稍后重新使用它:
对于非常简单的情况,此解决方案非常易于使用。假设您有:
在Java方面,你只需要做:
这让你不必编写 * 大量 * 样板代码!然而,应该注意字节顺序,如here所解释的。
arknldoa4#
Java不知道如何处理指针,但它应该能够存储来自本机函数返回值的指针,然后将其交给另一个本机函数处理。C指针只不过是核心的数值。
另一个贡献者必须告诉你在JNI调用之间是否会清除指向的图形内存,以及是否有任何变通方法。
jexiocij5#
我知道这个问题已经得到了官方的回答,但我想补充一下我的解决方案:不要尝试传递指针,而是将指针放在Java数组中(索引为0)并将其传递给JNI。JNI代码可以使用
GetIntArrayRegion
/SetIntArrayRegion
获取和设置数组元素。在我的代码中,我需要本机层来管理文件描述符(一个开放的套接字)。Java类保存一个
int[1]
数组并将其传递给本机函数。本机函数可以对它做任何事情(get/set),并将结果放回数组中。yeotifhr6#
如果在本机函数内部动态分配内存(在堆上),则不会删除它。换句话说,您可以使用指针、静态变量等来保留对本机函数的不同调用之间的状态。
换个Angular 想想:你能做什么安全地保存在一个函数调用中,从另一个C++程序调用?同样的事情也适用于这里。当一个函数退出时,该函数调用的堆栈上的任何内容都将被销毁;但是堆上的任何东西都会被保留,除非你显式地删除它。
简短回答:只要你不释放你返回给调用函数的结果,它将保持有效,以便以后重新进入。完事后记得清理干净。
j2cgzkjk7#
最好完全按照Unsafe.allocateMemory的方式来执行此操作。
创建你的对象,然后将它输入到(uintptr_t),这是一个32/64位的无符号整数。
这是唯一正确的做法。
下面是Unsafe.allocateMemory所做的健全性检查。
3hvapo4f8#
下面是另一种方法,在java-c C glue文件中使用全局
static
变量,通过引用将变量传递给C,并返回已从C更改为Java的变量。需要两个Java函数调用。下面是一个在Kotlin中使用Android开发的示例:
首先,C代码(一个简单的函数,递增通过引用传递的变量):
然后我们通常会像胶水一样使用另一个C代码来管理Java的白痴特性
它使用一个全局变量
number
(由于static
关键字,只能在文件glue. c中访问),并且可以使用getter函数getnumber
从外部访问。所以基本上我们需要一个额外的函数和一个静态全局变量来调用一个需要指针并改变其引用的C库。CMakeLists.txt
看起来像这样:最后是Android(Kotlin)部分:
它应该显示
5
。