内存映射文件的性能/稳定性 - Native或MappedByteBuffer - 与普通的'FileOutputStream相比

时间:2009-02-11 15:21:09

标签: java performance file-io java-native-interface production

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

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

我对此有几个问题:

  1. 这个断言真的如此吗? 内存映射文件始终 与Java相比,提供更快的IO FileOutputStream中?

  2. 类MappedByteBuffer 从FileChannel提供访问 与原生相同的功能 访问内存映射文件库 通过JNI?什么是MappedByteBuffer 缺乏可能导致你使用 JNI解决方案?

  3. 使用的风险是什么? 生产中磁盘IO的内存映射文件 应用?也就是应用程序 持续正常运行时间 最小的重启(每月一次,最大)。 来自生产的真实轶事 应用程序(Java或其他) 优选的。

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

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

7 个答案:

答案 0 :(得分:5)

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

NIO映射缓冲区是真实的(关于任何合理实现的常见警告)。

与其他NIO直接分配的缓冲区一样,缓冲区不是普通内存,也不会有效地进行GCed。如果您创建了许多这些内容,您可能会发现内存/地址空间不足而不会耗尽Java堆。这显然是一个长期运行过程的担忧。

答案 1 :(得分:4)

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

如果您只是从应用程序向磁盘写入新数据,则内存映射I / O可能无济于事。我认为你不想在一些自定义编码的原生解决方案上投入时间。从您目前提供的内容来看,您的应用程序似乎过于复杂。

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

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

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

答案 2 :(得分:2)

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

有关基于内存映射文件构建的持久性引擎的示例,请参阅Metakit。我在一个应用程序中使用它,其中对象是对内存映射数据的简单视图,引擎负责窗帘后面的所有映射。这既快速又节省内存(至少与之前版本使用的传统方法相比),我们免费提交了提交/回滚事务。

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

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

希望它有所帮助。

答案 3 :(得分:1)

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

顺便说一下,this SO discussion也可以为您提供一些见解。

答案 4 :(得分:1)

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

答案 5 :(得分:0)

如果你写的字节更少,它会更快。如果您通过gzipoutputstream过滤它,或者如果您将数据写入ZipFiles或JarFiles,该怎么办?

答案 6 :(得分:0)

如上所述,使用NIO(a.k.a。新IO)。还有一个新的,新的IO出现。

正确使用RAID硬盘驱动器解决方案对您有所帮助,但这将是一件痛苦的事。

我非常喜欢压缩数据的想法。去找gzipoutputstream老兄!如果CPU可以跟上,那么这会使吞吐量翻倍。您可以利用现在标准的双核机器,嗯?

-Stosh