我们正在验证XML文件,根据验证结果,我们必须将文件移动到另一个文件夹中。
当XML有效时,验证器返回一个值,我们可以毫无问题地移动文件。当XML根据模式无效时,也会发生同样的事情。
但是,如果XML格式不正确,验证器会抛出异常,当我们尝试移动文件时,它会失败。我们相信在某个地方仍然有一个句柄可以保存文件。我们在移动文件之前尝试放置System.gc()并对问题进行排序,但我们不能将System.gc()
作为解决方案。
代码看起来像这样。我们有一个File对象,我们从中创建一个StreamSource。然后将StreamSource传递给验证器。当XML格式不正确时,它会抛出SAXException。在异常处理中,我们使用.renameTo()方法移动文件。
sc = new StreamSource(xmlFile);
validator.validate(sc);
在捕获中我们尝试了
validator.reset();
validator=null;
sc=null;
但仍然.renameTo()
无法移动文件。如果我们将System.gc()
放入捕获中,则移动将成功。
有人可以告诉我如何在没有System.gc()?
我们使用JAXP和saxon-9.1.0.8作为解析器。
非常感谢
答案 0 :(得分:5)
尝试创建FileInputStream
并将其传递到StreamSource
,然后在完成后关闭FileInputStream
。通过传入File
,您无法控制关闭文件句柄的方式/时间。
答案 1 :(得分:3)
当您设置sc = null
时,您向垃圾收集器指示StreamSource文件已不再使用,并且可以收集它。 Streams在他们的destroy()
方法中关闭自己,所以如果它们被垃圾收集,它们将被关闭,因此可以在Windows系统上移动(在Unix系统上你不会遇到这个问题)。
要在不手动调用GC的情况下解决问题,只需在sc.getInputStream().close()
之前调用sc = null
即可。无论如何,这是一种很好的做法。
常见的模式是围绕任何文件句柄使用try .. finally
块,例如
try {
sc = new StreamSource(xmlFile);
// check stuff
} finally {
sc.getInputStream().close();
}
// move to the appropriate place
在Java 7中,您可以使用新的try with resources块。
答案 2 :(得分:1)
在catch
中尝试sc.getInputStream()。close()答案 3 :(得分:1)
已经给出的所有三个答案都是正确的:您必须关闭基础流,或者直接调用StramSource,或者获取流并关闭它,或者自己创建流并关闭它。
但是,我已经在windows下看到过这种情况至少三年了:即使你关闭了流,真的是每一个流,如果你试图移动或删除文件,它都会抛出异常..除非...你明确地调用了System.gc()。
但是,由于System.gc()并不是JVM实际执行一轮垃圾收集所必需的,因为即使是JVM没有强制要求删除所有可能的垃圾对象,你也没有真正的方法。确保文件可以“立即”删除。
我没有明确的解释,我只能想象,java.io的windows实现可能会以某种方式缓存文件句柄并且不会关闭它,直到句柄被垃圾收集。
据报道,但我还没有确认,java.nio不受此行为影响,因为它对文件描述符有更低级别的控制。
我过去使用过的解决方案是:
通常情况下,“滞后”大约是几毫秒,文件的一些例外可以存活一些。
也可以在这些文件上调用deleteOnExit,这样如果JVM在线程完成清理某些文件之前终止,JVM将尝试删除它们。但是,deleteOnExit当时有它自己的bug,完全阻止了文件的删除,所以我没有。也许今天已经解决了,你可以信任deleteOnExit。
这是我发现最烦人和愚蠢的JRE错误,并且不能相信它仍然存在,但不幸的是我在一个月前在安装了最新JRE的Windows Vista上点击它。
答案 4 :(得分:0)
相当古老,但有些人可能仍然会发现这个问题。
StreamSource
实例化的File
似乎在验证器或转换器处理时自动分配和释放文件资源。 (getInputStream()
返回null
)解决方案/解决方法:使用
移动文件Files.move(from.toPath(), to.toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
这里使用ATOMIC_MOVE
是关键点。无论原因是什么,它都与Windows锁定文件的恼人行为有关。