我有一个小而简单的存储系统,可以通过内存映射文件访问。由于我需要处理超过 2GB 的空间,我需要一个具有固定大小(如 2GB)的 MappedByteBuffer 列表(由于不同的原因,我使用较少)。然后一切都相对简单:一个缓冲区映射到某个空间,比如 1GB,当我需要更多时,我映射一个新的 MappedByteBuffer(文件自动增加),然后当我需要更多时,映射第三个缓冲区等等。这只是工作。
但是后来我在Java NIO 书中读到,当我更改文件长度时可能会出现问题:
MappedByteBuffer 直接反映与其关联的磁盘文件。如果在映射生效时文件在结构上被修改,可能会导致奇怪的行为(具体行为取决于操作系统和文件系统) MappedByteBuffer 具有固定大小,但它映射到的文件是弹性的。具体来说,如果在映射生效时文件的大小发生变化,则部分或全部缓冲区可能变得不可访问,可能会返回未定义的数据,或者可能会引发未经检查的异常。请注意文件在内存映射时如何被其他线程或外部进程操作。
我认为问题可能会发生,因为操作系统可以在文件增加时移动文件,然后 MappedByteBuffers 可能指向无效空间(或者我是否误解了这一点?)
因此,我现在没有向列表中添加新的 MappedByteBuffer,而是执行以下操作
- 增加文件长度
- 清除缓冲区列表(丢弃旧缓冲区并希望通过垃圾收集器释放缓冲区。嗯,也许我应该通过cleaner.clean()显式清除所有缓冲区?)
- 重新映射(用新的缓冲区填充列表)
但是这个过程的缺点是有时会在映射时失败
IOException: Operation not permitted
at sun.nio.ch.FileChannelImpl.map0(Native Method)
at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:734)
为什么?因为清除缓冲区列表没有正确释放和清理缓冲区并且不允许多个映射?我应该坚持旧的工作方法而忽略书中的评论吗?
更新