【问题标题】:IO errors using memory mapped files in Java在 Java 中使用内存映射文件的 IO 错误
【发布时间】:2012-09-26 21:21:21
【问题描述】:

我在一些 Java 代码中使用内存映射文件来快速写入 2G 文件。我将整个文件映射到内存中。我的解决方案遇到的问题是,如果我正在写入的文件神秘地消失了,或者磁盘出现了某种类型的错误,那么这些错误不会冒泡到 Java 代码中。

事实上,从Java代码来看,我的写似乎是成功完成的。这是我为模拟此类故障而创建的单元测试:

File twoGigFile = new File("big.bin");
RandomAccessFile raf = new RandomAccessFile(twoGigFile, "rw");
raf.setLength(Integer.MAX_VALUE);
raf.seek(30000); // Totally arbitrary
raf.writeInt(42);
raf.writeInt(42);

MappedByteBuffer buf = raf.getChannel().map(MapMode.READ_WRITE, 0, Integer.MAX_VALUE);
buf.force();
buf.position(1000000); // Totally arbitrary
buf.putInt(0);

assertTrue(twoGigFile.delete());

buf.putInt(0);
raf.close();

此代码运行时完全没有任何错误。这对我来说是一个相当大的问题。我似乎找不到任何关于此类问题的内容。有谁知道如何让内存映射文件正确抛出异常?或者是否有其他方法可以确保数据实际写入文件?

我试图避免使用 RandomAccessFile,因为它们比内存映射文件慢得多。但是,可能没有其他选择。

【问题讨论】:

    标签: java exception memory-mapped-files


    【解决方案1】:

    你不能。引用 JavaDoc:

    映射字节缓冲区的全部或部分可能随时变得不可访问 [...] 尝试访问映射字节缓冲区的不可访问区域不会更改缓冲区的内容,并且会导致引发未指定的异常在访问时或稍后的某个时间。

    原因如下:当您使用映射缓冲区时,您更改的是内存,而不是文件。在操作系统尝试将缓冲的块写入磁盘之前,内存恰好由文件支持这一事实是无关紧要的,这完全由操作系统管理(即,您的应用程序不会知道它正在发生)。

    如果您希望文件在您下方消失,那么您必须使用替代机制来查看是否会发生这种情况。一种可能性是偶尔使用RandomAccessFile 触摸文件,并捕获它将抛出的错误。根据您的操作系统,即使这样也可能还不够:例如,在 Linux 上,对于那些具有打开句柄的程序来说,存在一个文件,即使该文件已被外部删除。

    【讨论】:

    • 如果你注意到了,我告诉内存映射缓冲区强制将更改写入磁盘。这应该尽早创建异常,而不是等待操作系统将页面从内存写入磁盘。
    • 另一件事,我在测试中的删除只是一个易于测试的案例。我不希望这种情况永远发生。磁盘损坏、崩溃、卸载等的可能性更大。不过,这些情况更难测试。
    • 我不知道你为什么认为force() 会抛出异常,它没有被声明这样做。在幕后(至少在 POSIX 上),它调用 msync,它只报告调用错误。
    • 您可能还会问自己,如果更改被写入驱动器的前端缓存,但永远不会写入盘片,您将如何处理这种情况。或者所有内容都被写入,磁盘立即出现故障的情况。但是,如果这些情况对您很重要,那么一个简单的内存映射文件是不够的。
    • parisfal - MappedByteBuffer 的约定是它可以在任何时候抛出未知和未记录的异常。我认为 force 可以解决问题,因为该方法的约定是强制写入磁盘,显然它根本不会这样做,否则会报告错误。
    猜你喜欢
    • 2011-04-22
    • 2010-11-04
    • 2011-05-14
    • 2011-02-27
    • 2021-02-08
    • 2014-04-04
    • 1970-01-01
    • 1970-01-01
    • 2016-02-25
    相关资源
    最近更新 更多