在Java中,类似于mmap的访问内存的最佳方式是什么?

17

我正在开发一个需要与C应用程序通信的Java应用程序。C应用程序使用共享内存和mmap进行通信,我需要让Java应用程序能够访问相同的内存。

我的第一次尝试是使用JNI调用从共享内存中检索数据,但每个JNI调用的开销影响了性能,因此我希望找到一种方法在Java中访问该内存并在Java端进行数据检索。

我想要的解决方案如下:

  1. 使用一个JNI调用来获取我需要连接到的共享内存位置
  2. 创建一个新的FileChannel()
  3. 使用该FileChannel使用map()创建一个MappedByteBuffer

这是最好的方法吗?而且,我不确定如何实际创建FileChannel以指向正确的内存位置。


在我看来,你不能这样做,但我很乐意接受更正。 - dfa
5个回答

11

建议尝试使用ByteBuffer.allocateDirect。 显然,这些缓冲区可以通过JNI层传递到本机代码,以直接访问内存。

查看此页面(下面引用的内容)获取相关提示。

现在,不仅可以在Java端创建带有ByteBuffer.allocateDirect()的缓冲区内部的本机内存空间地址,而且还可以分配自己的内存(例如使用malloc()),然后回调到JVM将该内存空间包装在新的ByteBuffer对象中(执行此操作的JNI方法为NewDirectByteBuffer())。


1
这将是Java中最接近底层的编程。不幸的是,ByteBuffer仍然需要在虚拟机边界上获取信息。查看JDK如何实现Deflator可能会很有启发性- http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/Deflater.html - Kevin Day
2
我最终使用了ByteBuffer,并且它们的表现非常出色。在JNI代码中,我创建了一个ByteBuffer并将其返回给Java代码,然后Java代码可以直接访问该内存。最终,我的Java应用程序可以读取内存并向其写入数据。任何使用相同内存的C/C++应用程序都可以立即看到这些写入。 - Brian

1

我会编写一个小的C模块来映射共享内存,并使用套接字让另一个应用程序可以查看该内存(例如您的Java应用程序)。


0

你能否编写一个带有一些本地方法的Java类,使用C编写本地方法实现以使用mmap完成所需操作。然后将其编译为本地库,并使用LD_LIBRARY_PATH将其添加到运行时中。这将使您能够在Java中进行本地C调用,而无需JNI开销(我想是这样的)。

这里有一些教程:link

例如,您可以编写一个Java类,如下:

JMMap.java

public class JMMap {
    public native void write(...)
}

然后运行javah来生成类文件,类似于:
JMMap.h

JNIEXPORT void JNICALL Java_JMMap_write(JNIEnv *, jobject);

在 .c 文件中实现这个功能。将其编译为库文件并添加到 LD 路径中,然后只需调用 Java 函数即可。


0
如果您拥有C和Java应用程序,则它们可以通过控制台流进行通信 - 对于二进制数据来说可能不太美观,但是仍然可行。我建议使用更多的消息传递方式来交换共享内存 - 例如TCP / IP套接字对。
顺便问一下,传递的是什么类型的数据以及大小是多少?

我不确定确切的大小;可能大约在800Mb到超过1Gb。不幸的是,我没有修改C应用程序的余地。 - Brian
创建一个包装原始C应用程序的新C应用程序。一旦你这样做了,kd304的解决方案或AlbertoPL的解决方案都是可能的。 - NamshubWriter
在应用之间总是需要移动整个数据吗? - akarnokd
1
数据根本不需要移动,我只需要能够从C应用程序使用的共享内存中读取。 - Brian

-2
如果您能在C中将数据保存到文件中,然后从Java访问该文件,那将是最好的。据我所知,您无法使用Java以您想要的方式指向内存。

这是错误的。直接字节缓冲区提供了所需的功能。 - Ted Dunning

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接