java jni c 指针_通过JNI传递C和Java之间的指针

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

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

我的想法是通过将数据保留在GPU内存中,并只传递一个对它的引用,以便其他函数可以使用它,从而减少在GPU之间复制数据所产生的开销。

在尝试了一段时间后,我自己想,这不可能,因为指针在应用程序结束后被删除(在本例中,当C函数终止时)。这是正确的吗?或者我只是为了看问题的解决方案而在C中做得太差了?

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

感谢您的输入:)

另外stackoverflow.com/q/5802340/632951

我采用了以下方法:

在JNI代码中,创建一个结构来保存对所需对象的引用。当您首先创建此结构时,将其指针返回给Java作为EDCOX1 0。然后,从Java中调用这个EDCOX1×0作为参数的任何方法,并在C中将其转换为指向结构的指针。

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

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

我就是这样做的——我把指针回传给Java很长时间,然后把它传递给C。但是我怎么告诉C,它刚刚收到的是一个地址?long ptr=(long)&address;这是我尝试的方法,但我没有得到应该在地址处的值,我只得到地址本身(或其他一些地址,但没有值:()

…在ptr之前和第二个long之后应该有星号…

MyClass *pObject = ...; long lp = (long)pObject; pObject = (*pObject)lp;

在x32和x64平台上使用long?我很高兴我们不会很快转移到128位机器上…

你好,Denis Tulskiy,我有一个关于把Java中的长指针转换成一个数组的问题,我在同一个概念中使用了ITK,我得到了一个指向图像数据的指针,但是我不知道如何从指针中获得数组。我试图使用com.sun.jna.pointer,但我不知道gow会使它指向与传递的指针引用的数据相同的数据。

我和达迪有同样的关系。这个解决方案是不可移植的——不能保证long的大小足以容纳指针。

丹尼斯,你能写一个伪/样本代码来理解这一点吗?另外,用类对象代替结构,这是可行的吗?我在跟踪这件事。请建议。

sun.misc.unsafe大量使用long作为指针,因此它得到了有效的支持,不管Oracle是否真的批准它…所以,接受合同的风险由你自己承担。

我也分享了@dhardy的关注。一种方法是传递一个Java字节数组,其中数组中的每个字节是指针值的8位(并且根据系统使数组的长度不同)。但是,这意味着您还必须将指针的大小传递给JNI函数。

@Alexandernajafi:在这种情况下,我认为最好有一个已知大小的数据密钥。从键到指针保持哈希映射,并将密钥从本机代码返回到Java。

在C++中,您可以使用任何要分配/释放内存的机制:堆栈、MalC/C、Field、NeX/Delphi或任何其他自定义实现。唯一的要求是,如果使用一个机制分配一个内存块,则必须使用相同的机制释放它,这样就不能在堆栈变量上调用free,也不能在malloced内存上调用delete。

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

新建对象/删除本地引用

新全局参考/删除全局参考

newweakglobalref/删除weakglobalref

它们遵循相同的规则,唯一的缺点是本地引用可以"集中"删除,可以使用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;

...

}

难道(*pObject)lp;不是(MyClass*)lp;吗?

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

另一个Continibutor必须告诉您,在JNI调用之间是否清除指向图形内存,以及是否有任何解决方法。

关于您的第二段:唯一需要注意的是确保分配的内存也得到释放。建议的方法是在保存引用的对象上使用某种close/dispose()方法。定稿人很有吸引力,但他们也有一些缺点,如果可能的话,避免定稿是值得的。

我不应该写"唯一的东西"顺便说一句…JNI满是要掉进的坑。

我已经包含了释放分配内存的函数,所以应该没有问题:)主要的问题仍然是:如果我释放网络,内存是否保持分配状态?我的意思是,包括地址和值……我知道如果我不这样做,我将得到内存泄漏等,所以我已经包括了;-)

@沃克:这取决于你如何分配。您必须自己解除分配/释放内存的规则的唯一例外是,如果在堆栈上分配了某些内容,则通常是这样。如果是,则当堆栈指针在函数出口处向后移动时,它将被"释放"。因此,如果使用某种内存分配函数(alloca除外)分配内存,则必须释放它。它与Java无关。一旦JNI跳转,规则就从C世界开始,除非您使用Java对象。

弗雷德里克,你的回答是正确的,真的应该进入到它自己的答案中去。

"C指针只不过是核心的long值。"—C标准中没有这样说,这是在调用未定义的行为。

我知道这个问题已经得到了正式答复,但我想补充我的解决方案:不要尝试传递指针,而是将指针放在Java数组(索引0)中,并将其传递给JNI。JNI代码可以使用GetIntArrayRegion/SetIntArrayRegion获取和设置数组元素。

在我的代码中,我需要本机层来管理一个文件描述符(一个打开的套接字)。Java类保存一个EDCOX1×9的数组,并将其传递给本机函数。本机函数可以对其执行任何操作(get/set),并将结果放回数组中。

在Java中如何将传输的长指针转换为数组

虽然@denis tulskiy接受的答案确实有道理,但我还是个人遵循了这里的建议。

因此,不要使用伪指针类型,如jlong(如果要在32位arch上节省一些空间,请使用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);

对于非常简单的情况,这个解决方案非常容易使用。假设你有:

struct {

int exampleInt;

short exampleShort;

} MyNativeStruct;

在Java方面,您只需要这样做:

public int getExampleInt() {

return bb.getInt(0);

}

public short getExampleShort() {

return bb.getShort(4);

}

这样可以避免编写大量样板代码!但是,我们应该注意这里解释的字节顺序。

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

想一想另一种方式:你能做什么安全地保持在一个函数调用,从另一个C++程序调用?这里同样适用。当函数退出时,该函数调用的堆栈上的任何内容都将被销毁;但除非显式删除,否则将保留该堆栈上的任何内容。

简短回答:只要不取消分配返回到调用函数的结果,它将在稍后重新进入时保持有效。完成后一定要清理干净。

最好做到这一点,这正是.allocateMemory的不安全之处。

创建对象,然后将其键入(uintptr_t),它是一个32/64位无符号整数。

return (uintptr_t) malloc(50);

void * f = (uintptr_t) jlong;

这是唯一正确的方法。

这是健全性检查不安全。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

这不是标准对uintptr_t的定义。这样做是个很坏的主意。它被定义为足够大以容纳任何指针,并且可以是所需的任何长度。通常在64位系统上,这是64位的,但是标准甚至不允许这样的假设。你不应该对uintptr_t的大小做任何假设。

这就是JVM在32位和64位系统上分配内存的方式。分配uintptr_t允许干净地转换为jlong。我不想讨论这是否是一种好的方式,但这是JVM的方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值