内存映射文件(本机或 MappedByteBuffer)的性能/稳定性与内存映射文件的性能/稳定性比较普通的 FileOutputStream

StackOverflow https://stackoverflow.com/questions/537295

我支持使用平面文件(纯文本)进行持久化的旧版 Java 应用程序。由于应用程序的性质,这些文件的大小每天可以达到 100 MB,并且应用程序性能的限制因素通常是文件 IO。目前,应用程序使用普通的 java.io.FileOutputStream 将数据写入磁盘。

最近,我们有几位开发人员断言,使用以本机代码 (C/C++) 实现并通过 JNI 访问的内存映射文件将提供更高的性能。但是,FileOutputStream 已经使用本机方法作为其核心方法(即write(byte[])),因此这似乎是一个脆弱的假设,没有硬数据或至少没有轶事证据。

我对此有几个问题:

  1. 这个说法真的是真的吗?将内存映射文件 总是与Java的FileOutputStream相比,提供更快的IO?

  2. 从Filechannel访问的类MappedByteBuffer是否提供与通过JNI访问的本机内存映射文件库相同的功能?什么可能会导致您使用JNI解决方案的MapbyTebuffer什么?

  3. 在生产应用程序中将内存映射文件用于磁盘IO的风险是什么?也就是说,随着最少的重启(每月一次,最大一次),具有连续正常运行时间的应用程序。首选的生产应用程序(Java或其他)的现实生活轶事。

问题 #3 很重要 - 我可以自己回答这个问题 部分地 通过编写一个“玩具”应用程序,使用上述各种选项来执行 IO 测试,但通过发布到 SO,我希望能够了解现实世界的轶事/数据。

[编辑] 澄清 - 每天运行时,应用程序都会创建多个文件,大小范围从 100MB 到 1 gig。总的来说,应用程序每天可能会写入多千兆数据。

有帮助吗?

解决方案

通过检查写入期间数据的缓冲方式,您也许可以加快速度。这往往是特定于应用程序的,因为您需要了解预期的数据写入模式。如果数据一致性很重要,那么这里就需要进行权衡。

如果您只是从应用程序将新数据写入磁盘,内存映射 I/O 可能不会有太大帮助。我不认为您有任何理由愿意在某些自定义编码的本机解决方案上投入时间。从您到目前为止所提供的内容来看,这对于您的应用程序来说似乎太复杂了。

如果您确定确实需要更好的 I/O 性能 - 或者在您的情况下仅需要 O 性能,我会研究硬件解决方案,例如调整的磁盘阵列。从业务角度来看,投入更多硬件来解决问题通常比花时间优化软件更具成本效益。它通常实施起来更快并且更可靠。

总的来说,软件的过度优化存在很多陷阱。您将为您的应用程序引入新类型的问题。您可能会遇到内存问题/GC 抖动,这会导致更多的维护/调整。最糟糕的是,其中许多问题在投入生产之前很难进行测试。

如果这是我的应用程序,我可能会坚持使用 FileOutputStream 以及一些可能调整的缓冲。之后,我将使用历史悠久的解决方案,即向其投入更多硬件。

其他提示

内存映射 I/O 不会使您的磁盘运行得更快(!)。对于线性访问来说,这似乎有点毫无意义。

NIO 映射缓冲区是真实存在的(通常需要注意任何合理的实现)。

与其他 NIO 直接分配的缓冲区一样,缓冲区不是普通内存,并且不会有效地进行 GC。如果您创建了许多它们,您可能会发现内存/地址空间耗尽,而 Java 堆却没有耗尽。这显然是长时间运行的进程的一个担忧。

根据我的经验,在实时和持久性用例中,内存映射文件的性能比普通文件访问要好得多。我主要在 Windows 上使用 C++,但 Linux 性能相似,并且您无论如何都计划使用 JNI,所以我认为它适用于您的问题。

有关基于内存映射文件构建的持久性引擎的示例,请参阅 元工具包. 。我在一个应用程序中使用了它,其中对象是内存映射数据的简单视图,引擎负责幕后的所有映射工作。这既快速又高效(至少与以前版本使用的传统方法相比),并且我们免费获得提交/回滚事务。

在另一个项目中,我必须编写多播网络应用程序。数据以随机顺序发送,以尽量减少连续丢包的影响(结合 FEC 和阻塞方案)。此外,数据很可能超出地址空间(视频文件大于 2Gb),因此内存分配是不可能的。在服务器端,文件部分按需进行内存映射,网络层直接从这些视图中选取数据;因此,内存使用率非常低。在接收方,无法预测接收数据包的顺序,因此它必须在目标文件上维护有限数量的活动视图,并将数据直接复制到这些视图中。当必须将数据包放入未映射的区域时,最旧的视图将被取消映射(并最终由系统刷新到文件中)并被目标区域上的新视图替换。性能非常出色,特别是因为系统在将数据作为后台任务提交方面做得很好,并且很容易满足实时约束。

从那时起,我确信即使是最好的精心设计的软件方案也无法击败系统使用内存映射文件的默认 I/O 策略,因为系统比用户空间应用程序更了解何时以及如何写入数据。另外,重要的是要知道,在处理大数据时,内存映射是必须的,因为数据永远不会被分配(因此消耗内存),而是动态映射到地址空间,并由系统的虚拟内存管理器管理,即总是比堆快。因此,系统始终以最佳方式使用内存,并在需要时在应用程序背后提交数据,而不会影响应用程序。

希望能帮助到你。

至于第 3 点 - 如果机器崩溃并且有任何页面未刷新到磁盘,那么它们就会丢失。另一件事是地址空间的浪费 - 将文件映射到内存会消耗地址空间(并且需要连续的区域),而且在 32 位机器上它有点有限。但你说的是 100MB 左右 - 所以这应该不是问题。还有一件事 - 扩展 mmaped 文件的大小需要一些工作。

顺便一提, 这个SO讨论 也可以给你一些见解。

我做了一个 学习 我将写入性能与原始数据进行比较 ByteBuffer 与写入性能 MappedByteBuffer. 。操作系统支持内存映射文件,并且它们的写入延迟非常好,正如您在我的基准测试数据中看到的那样。通过 FileChannel 执行同步写入大约慢 20 倍,这就是人们一直进行异步日志记录的原因。在我的研究中,我还给出了一个示例,说明如何通过无锁和无垃圾队列实现异步日志记录,以获得非常接近原始性能的最终性能。 ByteBuffer.

如果写入的字节数较少,速度会更快。如果通过 gzipoutputstream 过滤它,或者将数据写入 ZipFiles 或 JarFiles 会怎样?

如上所述,使用 NIO(又名:新的IO)。还有一个新的、新的 IO 即将问世。

正确使用 RAID 硬盘解决方案会对您有所帮助,但这会很痛苦。

我真的很喜欢压缩数据的想法。去寻找 gzipoutputstream 老兄!如果 CPU 能够跟上的话,吞吐量将会翻倍。您很可能可以利用现在标准的双核机器,是吗?

-斯托什

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top