代码之家  ›  专栏  ›  技术社区  ›  noahlz

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

  •  11
  • noahlz  · 技术社区  · 17 年前

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

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

    我对此有几个问题:

    1. 这种说法是真的吗? 总是 文件输出流?

    2. 从FileChannel提供访问 通过JNI?什么是MappedByteBuffer 缺乏这一点可能会导致你使用

    3. 使用有什么风险 生产环境中磁盘IO的内存映射文件 应用程序?也就是说,应用程序 具有连续正常运行时间 最小重启次数(最多每月一次)。 生产中的真实轶事 首选。

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

    [编辑]澄清-应用程序每天都会创建多个文件,大小从100MB到1GB不等。总的来说,应用程序每天可能会写出多个数据。

    7 回复  |  直到 17 年前
        1
  •  6
  •   Tom Hawtin - tackline    17 年前

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

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

    与其他NIO直接分配的缓冲区一样,这些缓冲区不是正常的内存,也不会有效地进行GC。如果您创建了许多这样的程序,您可能会发现内存/地址空间不足,而Java堆没有用完。对于长时间运行的流程,这显然是一个令人担忧的问题。

        2
  •  4
  •   Gary    17 年前

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

    如果您只是从应用程序向磁盘写入新数据,内存映射I/O可能不会有多大帮助。我看不出你有任何理由想在一些定制编码的本机解决方案上投入时间。从您目前提供的内容来看,这对您的应用程序来说似乎太复杂了。

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

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

    如果这是我的应用程序,我可能会坚持使用FileOutputStream,并可能对缓冲区进行一些调整。之后,我会使用历史悠久的解决方案,向它投入更多的硬件。

        3
  •  3
  •   fbonnet    17 年前

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

    有关基于内存映射文件构建的持久性引擎的示例,请参见 Metakit

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

    希望它能有所帮助。

        4
  •  1
  •   Community Mohan Dere    9 年前

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

    顺便说一句, this SO discussion 也可以给你一些见解。

        5
  •  0
  •   Ron    17 年前

        6
  •  0
  •   johnstosh    17 年前

    如上所述,使用NIO(也称为新IO)。还有一个新的IO即将推出。

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

    我真的很喜欢压缩数据的想法。去找gzipoulputstream的家伙!如果CPU能够跟上,这将使您的吞吐量翻倍。你很可能可以利用现在标准的双核机器,对吧?

        7
  •  0
  •   TraderJoeChicago    13 年前

    我做了一个 study 在那里,我将写入性能与原始性能进行比较 ByteBuffer 与写入性能相比 MappedByteBuffer ByteBuffer .