我在 Java 中有一个对象数组,我想允许本机 C 代码直接访问该数组(没有复制或访问器函数)。这可能吗?我不介意解决方案是否特定于 JVM。
4 回答
当然,使用 JNI 是可能的。
值得咨询此链接:http: //journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/array.html 简而言之 -
#include <jni.h>
#include "IntArray.h"
JNIEXPORT jint JNICALL
Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
jsize len = (*env)->GetArrayLength(env, arr);
int i, sum = 0;
jint *body = (*env)->GetIntArrayElements(env, arr, 0);
for (i=0; i<len; i++)
sum += body[i];
(*env)->ReleaseIntArrayElements(env, arr, body, 0);
return sum;
}
如果您使用较新版本的 java,请使用ByteBuffer对象。
调用ByteBuffer.allocateDirect()
分配缓冲区。直接缓冲区位于垃圾收集器的域之外。要从 JNI 访问缓冲区,请调用GetDirectBufferAddress()
. 它返回一个指向字节缓冲区的指针。这不会在引擎盖下进行复制。缓冲区的更改将在 Java 和 Native 端看到。
javadocs 有一些关于使用直接缓冲区的警告:
可以通过调用此类的 allocateDirect 工厂方法来创建直接字节缓冲区。此方法返回的缓冲区通常比非直接缓冲区具有更高的分配和释放成本。直接缓冲区的内容可能驻留在正常的垃圾收集堆之外,因此它们对应用程序内存占用的影响可能并不明显。因此,建议将直接缓冲区主要分配给受底层系统的本机 I/O 操作影响的大型、长期存在的缓冲区。通常,最好仅在直接缓冲区对程序性能产生可测量的增益时才分配它们。
我终于找到了答案。通常不可能用 JNI 做我想做的事。但是,一些虚拟机提供了所需的功能,我使用了 JikesRVM:
1)本机代码需要数组的内存位置。这可以使用 JikesRVM 中的“Magic”工具来实现,它提供了一个 ObjectReference 和一个 Address 类,允许获取每个对象的内存地址。然后可以将该地址作为 long/int 参数(使用 JNI)转发到本机代码,并在那里转换为指针。
2) GC 可能不会移动对象。这有点棘手,因为它需要在 GC 中支持 pinning。在 JikesRVM 的情况下,可以使用 @NonMoving 和 @NonMovingAllocation 注释对象(也是“魔术”的一部分)。此外,大于 8KB 的对象(即数组)被放置在大对象空间中,不会移动对象。