2

我的 Java 应用程序使用内存映射文件(MappedByteBuffer、FileChannel 和 RandomAccessFile)处理大型二进制数据文件。它经常需要增加二进制文件——我目前的方法是用更大的区域重新映射文​​件。

它有效,但是有两个问题

  1. 随着文件变大,增长需要越来越多的时间。
  2. 如果增长非常迅速(EG 在 while(true) 循环中),JVM 将在重新映射操作完成大约 30,000 次后永远挂起。

有哪些替代方法,最好的方法是什么?

我也无法弄清楚为什么会出现第二个问题。还请就该问题提出您的意见。

谢谢!

用于增长文件的当前代码(如果有帮助):

(set! data (.map ^FileChannel data-fc FileChannel$MapMode/READ_WRITE
                         0 (+ (.limit ^MappedByteBuffer data) (+ DOC-HDR room))))
4

3 回答 3

5

您可能希望以更大的块增长您的文件。每次重新映射时使用加倍,例如动态数组,以便增长的成本是摊销常数。

我不知道为什么重映射在 30,000 次后挂起,这似乎很奇怪。但是,如果您使用我建议的方案,您应该能够摆脱少于30,000 次的重映射。

于 2012-09-14T06:44:41.393 回答
0

除非你能承受文件的指数增长,比如加倍,或者任何其他常数乘数,你需要考虑你是否真的需要 a MappedByteBuffer,考虑到它们的限制(无法增长文件,没有 GC 等)。我个人要么审查问题,要么使用RandomAccessFile“rw”模式,可能在其顶部有一个虚拟阵列层。

于 2012-09-14T10:16:18.827 回答
0

即使您显式调用清理程序,JVM 也不会清理内存映射。感谢@EJP 的更正。

如果你创造了 32,000 个这样的东西,它们可能会同时存在。顺便说一句:我怀疑你可能会达到 15 位的限制。

唯一的解决方案是;不要创建这么多映射。您可以使用小于 4K 的映射映射整个磁盘 4 TB 磁盘。

如果您知道使用量会增长,我不会创建小于 16 到 128 MB 的映射,并且我会考虑每个映射最多 1 GB。您可以以很少的成本执行此操作的原因是在您实际使用页面之前不会分配主内存和磁盘空间。即主内存使用量一次增长 4 KB。

我不会创建 2 GB 映射的唯一原因是由于 Integer.MAX_VALUE 大小限制,Java 不支持这一点:( 如果您有 2 GB 或更多,则必须创建多个映射。

于 2012-09-14T07:14:26.413 回答