Java 7的FileChannel在调用map方法后无法正确关闭。

5
我正在开发一个SC2Replay解析工具,它是在MPQLIB的基础上构建的 http://code.google.com/p/mpqlib/。不幸的是,该工具使用filechannels来读取bzip文件,并使用map(MapMode.READ_ONLY, hashtablePosition, hashTableSize);。调用该函数后关闭文件通道并没有释放进程中的文件。具体地说,我无法重命名/移动该文件。这个问题出现在Java 7中,在Java 6中可以正常工作。以下是一个简单的代码片段来复制它:
    FileInputStream f = new FileInputStream("test.SC2Replay");
    FileChannel fc = f.getChannel();

    fc.map(MapMode.READ_ONLY, 0,1);

    fc.close();

    new File("test.SC2Replay").renameTo(new File("test1.SC2Replay"));

注释掉 fc.map 可以让您重命名文件。
另外,从这里(Should I close the FileChannel?)得知,您无需同时关闭 filechannel 和 filestream,因为关闭其中一个将关闭另一个。我已经尝试过关闭任意一个或两个,但仍然没有效果。
在使用 Java 7 的 FileChannel.map 读取数据后,是否有绕过方法可以重命名文件?因为现在似乎每个人都在使用 Java 7。

关闭通道或流是否会抛出某种RuntimeException? - 0xCAFEBABE
没有生成任何异常。我认为这可能是Java 7本身的一个错误。我正在考虑将所有内容移植到.NET xD。 - user1717817
3个回答

2

您好,

看起来在Java 7中,FileChannel.map存在问题。如果使用FileChannel.map,那么您将无法关闭该文件。

一个快速解决方法是不再使用FileChannel.map(MapMode.READ_ONLY, position, length),而是使用:

ByteBuffer b = ByteBuffer.allocate(length); 
fc.read(b,position);
b.rewind();

0

这是一个已记录的错误。该错误报告涉及Java 1.4,并认为它是文档错误。关闭文件通道不会关闭底层流。


1
@tskaahyeah 这里提到的错误报告是可疑的,并标记为“无法重现”。关闭 FileChannel 会关闭底层文件描述符:我在本周早些时候阅读了源代码。这里的问题是关闭映射文件不会释放映射 - user207421
我尝试关闭FileChannel和FileInputStream,也尝试仅关闭FileInputStream和仅关闭FileChannel。所有方法都无效。该代码在Java6上运行良好,但在Java7上不起作用。 - user1717817

0
如果您正在使用Sun JRE,您可以通过将其转换为其实现并告诉它释放自己来作弊。我只建议在您不依赖于文件关闭或从未计划使用另一个JRE时才这样做。
希望在某个时候,类似这样的东西能够进入适当的公共API。
try (FileInputStream stream = new FileInputStream("test.SC2Replay");
     FileChannel channel = stream.getChannel()) {

    MappedByteBuffer mappedBuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, 1);
    try {
        // do stuff with it
    } finally {
        if (mappedBuffer instanceof DirectBuffer) {
            ((DirectBuffer) mappedBuffer).cleaner().clean();
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接