为什么Java FileChannel.truncate仅在Windows上可预测地失败?

时间:2017-08-01 14:56:31

标签: java file ioexception randomaccessfile filechannel

我有一个在Wildfly中运行的Web应用程序,部分应用程序使用临时文件: File.createTempFile(...)。 然后,该临时文件将用作读写java.io.RandomAccessFile流的目标文件。 RandomAccessFile的java.nio.channels.FileChannel是与之交互并最终被截断的内容(当我完成它时)。文件的某些部分通过FileChannel.map()

进行读写映射

完成与文件的所有交互后,使用fileChannel.truncate(size)将文件截断为适当的大小。这是总是抛出IOException的地方  at sun.nio.ch.FileDispatcherImpl.truncate0(Native Method)

IOException消息是通用的:
java.io.IOException: The requested operation cannot be performed on a file with a user-mapped section open

这是一个由同一个进程显式创建的临时文件,它在使用它的过程中一直保持打开状态,可能只需几秒钟。谷歌搜索表明可能AV软件有文件处理,或其他一些应用程序正在使用它,但我已尝试在各种VM和不同配置的常规窗口框上运行它,问题可靠地重现。我认为必须对文件工具进行一些真正的滥用,但我不知所措。

此外,在Linux机器上从未见过此异常,截断工作可靠。没有执行文件锁定,这是唯一正在使用的文件,正在缓冲并写入通道,但没有什么奇怪的事情发生。

为了记录,我创建了一些示例测试应用程序创建流,通道,临时文件,执行截断等,我无法在独立应用程序中重现该问题。不幸的是,我无法直接分享来源,因此我正在尽力描述发生了什么,希望有人遇到类似的事情并提供一些指导。

1 个答案:

答案 0 :(得分:3)

我的问题似乎没有正确的答案/解决方案,但至少我发现了一些可以解决问题的细节,这要归功于@diginoise发布的评论链接。

我得到了异常,因为我试图截断一个存储器映射字节缓冲区仍处于“活动状态”的文件。显然,用户无法手动取消映射/释放该内存上的Windows句柄(请参阅oracle的bug数据库中的JDK bug)。

最后,我认为答案只是在我完成这些文件时不尝试截断/删除这些文件,而是在下次应用程序启动时跟踪它们并处理它们,这样他们就可以保证没有活动的内存映射。

顺便说一下,File.deleteOnExit()在这些内存映射文件条件下也会失败。

在我实现下次启动时处理文件的更永久性修复之前,我已经通过反射选择了manually unmap内存。

这种缓解功能在不抛出错误的情况下运行,它允许截断,并且允许deleteOnExit,但这是一种短期不可持续的解决方法。根据FileChannel实现,使用反射来调用可能不存在的方法并不是一种好习惯。使用FilChannel的另一个实现,我可能已经破坏了永远不会被清理的临时文件。