【问题标题】:When changing the file length do I need to remap all associated MappedByteBuffers?更改文件长度时,是否需要重新映射所有关联的 MappedByteBuffers?
【发布时间】:2012-12-23 14:52:53
【问题描述】:

我有一个小而简单的存储系统,可以通过内存映射文件进行访问。由于我需要处理超过 2GB 的空间,我需要一个具有固定大小(如 2GB)的 MappedByteBuffer 列表(由于不同的原因,我使用较少)。然后一切都相对简单:一个缓冲区映射到某个空间,比如 1GB,当我需要更多时,我映射一个新的 MappedByteBuffer(文件自动增加),然后当我需要更多时,映射第三个缓冲区等等。这只是工作。

但后来我在Java NIO book 中读到,当我更改文件长度时可能会出现问题:

MappedByteBuffer 直接反映与其关联的光盘文件。如果在映射生效时文件在结构上被修改,可能会导致奇怪的行为(具体行为取决于操作系统和文件系统) MappedByteBuffer 具有固定大小,但它映射到的文件是弹性的。具体来说,如果在映射生效时文件的大小发生变化,部分或全部缓冲区可能变得不可访问,可能会返回未定义的数据,或者可能会引发未经检查的异常。请注意文件在内存映射时如何被其他线程或外部进程操作。

我认为问题可能会发生,因为操作系统可能会在文件增加时移动文件,然后 MappedByteBuffers 可能指向无效空间(或者我是否误解了这一点?)

所以,我现在不向列表中添加新的 MappedByteBuffer,而是执行以下操作

  1. 增加文件长度
  2. 清除缓冲区列表(丢弃旧缓冲区并希望通过垃圾收集器释放缓冲区。嗯,我可能应该通过 cleaner.clean() 显式清除所有缓冲区?)
  3. 重新映射(用新的缓冲区填充列表)

但是这个过程的缺点是有时会在映射时失败

IOException: Operation not permitted
    at sun.nio.ch.FileChannelImpl.map0(Native Method)
    at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:734)

为什么?因为清除缓冲区列表没有正确释放和清理缓冲区并且不允许多个映射?我应该坚持旧的工作方法而忽略书中的评论吗?

更新

  • 在 32 位操作系统上拆分映射的优点是可以更好地找到可用空间并且不太可能出错 (ref)
  • 将映射拆分成更小的部分是一个优势,因为设置 mmap 的成本可能很高 (ref)
  • 这两种方法都不干净,而我的第二种方法应该可以工作,但需要取消映射(将尝试使用正常的 cleaner.clean hack 强制释放)。第一种方法应该适用于我可以增加文件大小的系统(如ibm),但总的来说它不起作用,尽管我还没有找到确切的原因......
  • 最干净的方法是使用多个我担心的文件(每个 MappedByteBuffer 一个文件)

【问题讨论】:

  • 您能否澄清“当文件变得太大时,我会映射一个新的 MappedByteBuffer(文件自动增加)”。为什么不使用多文件方法来超过 2G 限制?顺便说一句,您以前的方法还可以,但是对于新方法,您完全可以按照书上的警告进行操作-更改文件大小。
  • 我澄清了你提到的那部分。要超过 2GB 限制,不必使用多个文件。带有 get/set(int) 的 Buffer API 就是限制我们的地方。为什么新方法正是这本书警告我的?我正在扔掉旧的缓冲区...
  • 在清除缓冲区列表之前是否增加文件大小?这可能是个问题。也可能是如果您尝试专门绘制同一区域的地图,这可能会导致“不允许操作”
  • 是的,我在清除之前增加了大小。但是我从哪里得到一些明确的解释呢?我不喜欢通过反复试验进行编程:)
  • 正如您想象的那样,这是一个以操作系统为中心的机制。因此,例如在 Linux 上学习 mmap 或在 Windows 上学习 mmap 可能会更好。

标签: java nio mmap memory-mapped-files


【解决方案1】:

根本原因是我的错:不小心我过于频繁地重新映射底层文件(容量仅通过小步骤增加)。

但即使在这种极端情况下,当我重试失败的映射操作(+ System.gc + 5ms 睡眠 -> 这应该让 jvm 有机会取消映射时,我终于能够修复 IOException(不允许操作)缓冲区)。现在我只看到了大量的重新映射,最终得出了结论。

至少我学到了更多关于 mmap 的知识:它非常依赖于操作系统 + 文件系统 - 还要感谢 auselen!如果您喜欢一个干净的解决方案,您应该按照他最初的建议为每个文件使用一个 MappedByteBuffer。但是,如果您需要大空间并且您的操作系统文件描述符限制太低,这也可能会出现问题。

最后但并非最不重要的一点是,我强烈建议不要使用我的第一个解决方案,因为我找不到保证(仅在 IBM 操作系统中;))在文件大小增加后保持映射缓冲区完好无损。

【讨论】:

猜你喜欢
  • 2012-09-17
  • 2020-10-31
  • 1970-01-01
  • 1970-01-01
  • 2012-10-05
  • 1970-01-01
  • 2015-08-23
  • 1970-01-01
  • 2016-12-28
相关资源
最近更新 更多