java - 更改文件长度时,是否需要重新映射所有关联的 MappedByteBuffer?

标签 java nio mmap memory-mapped-files

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

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

A MappedByteBuffer directly reflects the disc file with which it is associated. If the file is structurally modified while the mapping is in effect, strange behaviour can result (exact behaviour are OS and file system dependent) A MappedByteBuffer has a fixed size, but file it's mapped to is elastic. Specifically if a file's size changes while the mapping is in effect, some or all of the buffer may become inaccessible, undefined data could be returned, or unchecked exceptions could be thrown. Be careful about how files are manipulated by other threads or external processes when they are memory-mapped.

我认为问题可能会发生,因为操作系统可能会在文件增加时移动文件,然后 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 一个文件)

最佳答案

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

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

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

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

关于java - 更改文件长度时,是否需要重新映射所有关联的 MappedByteBuffer?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14011919/

相关文章:

c - 使用 mmap 在 C 中逐行读取文件的最佳方法?

java - 从内存映射文件并行读取数据?

java - .action 扩展...它是什么?

java - sbt 中的工作目录

Java NIO Zip 文件系统相当于 java.util.zip.ZipEntry 中的 setMethod()

java - 在 java 中寻找 "does everything buffer"- 决定使用 Netty

Java NIO Files count()方法统计行数

c - 为什么当偏移量为非负数(但是是 sysconf(_SC_PAGE_SIZE) 的倍数)时 mmap 会因 EINVAL 而失败?

java - 如何将文本 JPQL JPA 查询转换为条件查询?

java - 使用一些键在 android 中创建随机颜色(对于相同的键,它应该生成相同的颜色)