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

Why does Java FileChannel.truncate fail predictably on Windows only?

我在 Wildfly 中有一个 Web 应用程序 运行ning,该应用程序的一部分通过以下方式使用临时文件: File.createTempFile(...)。 然后将该临时文件用作读写 java.io.RandomAccessFile 流的目标文件。该 RandomAccessFile 的 java.nio.channels.FileChannel 是与之交互并最终被 t运行 处理的内容(当我完成它时)。文件的某些部分确实通过 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 和常规 windows 盒子上使用它是可靠地重现。我认为肯定存在一些对文件工具的真正滥用,但我不知所措。

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

郑重声明,我创建了一些示例测试应用程序来创建流、通道、临时文件、执行 t运行阳离子等,但我无法在独立应用程序中重现该问题。不幸的是,我不能直接分享源代码,所以我尽力描述正在发生的事情,希望有人 运行 有类似的东西并可以提供一些指导。

我的问题似乎没有正确的答案/解决方案,但至少我遇到了一些可以说明问题的细节,这要感谢 link @diginoise 作为评论发布。

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

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

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

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

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