3

我们正在验证 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 作为解析器。

非常感谢

4

5 回答 5

5

尝试创建 aFileInputStream并将其传递给StreamSource然后FileInputStream在完成后关闭。通过传入 aFile您已经失去了对如何/何时关闭文件句柄的控制。

于 2011-08-01T15:49:58.667 回答
3

当您设置 时sc = null,您向垃圾收集器指示不再使用 StreamSource 文件,并且可以收集它。流在他们的方法中关闭自己destroy(),所以如果它们被垃圾收集,它们将被关闭,因此可以在 Windows 系统上移动(在 Unix 系统上你不会遇到这个问题)。

要在不手动调用 GC 的情况下解决问题,只需调用sc.getInputStream().close()before 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块。

于 2011-08-01T15:48:56.270 回答
1

在 catch 中尝试 sc.getInputStream().close()

于 2011-08-01T15:49:44.737 回答
1

已经给出的所有三个答案都是正确的:您必须关闭底层流,或者直接调用 StramSource,或者获取流并关闭它,或者自己创建流并关闭它。

但是,至少三年以来,我已经在 Windows 下看到了这种情况:即使您关闭了流,实际上是每个流,如果您尝试移动或删除文件,它也会抛出异常.. 除非...您显式调用 System.gc()。

但是,由于 System.gc() 对于 JVM 实际执行一轮垃圾回收不是强制性的,并且即使它是 JVM 也没有被强制删除所有可能的垃圾对象,所以您没有真正的方法来确定该文件可以“现在”删除。

我没有明确的解释,我只能想象 java.io 的 windows 实现可能会以某种方式缓存文件句柄并且不会关闭它,直到句柄被垃圾收集。

据报道,但我还没有证实,java.nio 不受这种行为的影响,因为它对文件描述符有更多的低级控制。

我过去使用过的一个解决方案,但这是一个相当的黑客,是:

  1. 将要删除的文件放在“列表”上
  2. 让后台线程定期检查该列表,调用 System.gc 并尝试删除这些文件。
  3. 从列表中删除您设法删除的文件,并保留那些尚未准备好删除的文件。

通常“滞后”是几毫秒的数量级,除了一些文件的例外情况要多一点。

对这些文件也调用 deleteOnExit 可能是个好主意,这样如果 JVM 在您的线程完成清理某些文件之前终止,则 JVM 将尝试删除它们。但是,当时 deleteOnExit 有它自己的错误,完全阻止了文件的删除,所以我没有。也许今天它已经解决了,您可以信任 deleteOnExit。

这是我觉得最烦人和最愚蠢的 JRE 错误,不敢相信它仍然存在,但不幸的是,我一个月前在安装了最新 JRE 的 Windows Vista 上遇到了它。

于 2011-08-01T16:02:26.663 回答
0

很老了,但有些人可能仍然会发现这个问题。

  1. 我使用的是 Oracle Java 1.8.0_77。
  2. 问题发生在 Windows 上,而不是 Linux 上。
  3. StreamSource实例化的 a似乎File在由验证器或转换器处理时自动分配和释放文件资源。(getInputStream()返回null
  4. 在 Windows 上,处理后无法将文件移动到源文件的位置(删除源文件)。

解决方案/解决方法:使用移动文件

Files.move(from.toPath(), to.toPath(), REPLACE_EXISTING, ATOMIC_MOVE);

这里的使用ATOMIC_MOVE是关键点。不管是什么原因,它都与 Windows 锁定文件的烦人行为有关。

于 2016-04-06T13:15:17.920 回答