【问题标题】:How to diagnose File.delete() returning false / find unclosed streams?如何诊断 File.delete() 返回错误/查找未关闭的流?
【发布时间】:2010-01-24 19:56:45
【问题描述】:

我正在使用第 3 方 JPEG/EXIF 操作库 (Mediautil),这让我有些头疼。我想更改图像的 EXIF 数据。为此,我需要将更新版本写入临时文件,删除原始文件,然后将临时文件重命名为原始名称。

我的问题是File.delete() 调用失败并返回false,大概是因为库仍然以某种方式打开了它 - 但我已经完成了我在 API 中可以找到的一切以使其关闭所有流。更糟糕的是:问题似乎与时间有关,发生它的单元测试有时会失败,有时不会 - 但代码不是多线程的。

奇怪的是,有一个库调用可以解决问题 - 但它也删除了我实际上并不想要的 EXIF 缩略图。查看代码,我绝对看不出它在哪里关闭了任何可能保持打开状态的流。

任何想法如何解决这个问题?

编辑: 这是在 Windows XP、Java 6 上。还有一件事:我发现如果我在调用 File.delete() 之前调用 System.gc(),它会起作用 - 大概是因为这会触发一些终结器。所以它肯定看起来是一个未封闭的流。

【问题讨论】:

  • 您的机器上是否运行了防病毒软件?有时 AV 会在您编写文件后临时打开文件,从而导致您提到有时您在尝试删除时“不走运”的零星行为。
  • @Neil 好主意,但关闭 AV 并没有什么不同。

标签: java io


【解决方案1】:

我会在这里寻求调试器的帮助。对java.io 的快速挖掘表明,唯一可能的finalize() 候选对象是FileOutputStream。所以在那里打一个断点,运行你的程序,然后尝试让System.gc()触发FileOutputStream.finalize()释放你的流。这应该会给你一个关于这是否是你的问题的答案。

一旦你可以重现它,那么你需要开始匹配 FileOutputStream 实例的实例化和它们的终结。一个好的调试器将为您提供每个对象的内部 JVM 对象标识符,因此如果您可以在创建 OID 时跟踪它们,并在它们完成时跟踪它们,那么希望您能够将关键调用关联到 finalize使用特定的电话 new new FileOutputStream

不过,这可能是一个漫长的过程,具体取决于您的应用程序的复杂程度。

【讨论】:

  • 现在试试这个。烦人的是,我很确定罪魁祸首是 FileInputStream,而那些被 JVM 用来加载类...
  • 好的,我现在用这个方法找到了库中的泄漏。
【解决方案2】:

如果您使用的是 FileOutputStream,则显式关闭它允许删除文件。

例如而不是:

File myFile = new File("test.txt");
myCustomStreamProcess(new FileOutputStream(myFile));
boolean test = myFile.delete(); //May return false

你应该这样做:

File myFile = new File("test.txt");
FileOutputStream fos = new FileOutputStream(myFile);
myCustomStreamProcess(fos);
fos.close(); //Allow the document to be deleted afterwards
boolean test = myFile.delete(); //Should always return true

【讨论】:

  • 欢迎来到 Stack Overflow。在回答之前,您应该仔细阅读问题和现有答案,并确保您理解它们。您的回答没有帮助,因为它假定我正在编写使用 FileOutputStream 的代码;如问题所述,情况并非如此。
  • 感谢您的评论,您的问题帮助我找到了一些临时测试文件没有被删除但遗憾的是还无法投票的原因。我回答是希望它对遇到此问题的其他人有用,如果不合适,我可以删除它。
【解决方案3】:

为什么不在让库打开文件之前重命名文件?然后也许使用 Java 的 File.deleteOnExit() 删除重命名的文件。例如:

 File jpeg = new File("image.jpg");
 File temp = new File(jpeg + ".temp.jpg");
 jpg.renameTo(temp);
 SomeObj result = exifLibrary(temp); // or exifLibrary(new FileInputStream(temp);
 OutputStream jpegStream = new FileOutputStream(jpeg);
 output.write(result.bytes();
 output.close();
 temp.deleteOnExit();
 temp.delete();

【讨论】:

  • 问题是不是所有读取的文件都需要更新EXIF数据。
【解决方案4】:

查看代码,我完全看不出它在哪里关闭了任何可能保持打开状态的流。

我认为您可以确定真正的问题;即您正在使用的 API 会根据设计泄漏打开文件句柄。

由于这是您使用的开源库,因此您可以访问源代码。因此,您应该能够确认这一点,并在必要时自行修复。为了成为一个好公民,请通过提交补丁将您的修复贡献回项目。

【讨论】:

  • 我已经在谈论查看库的源代码,而不是 API。令人费解的是,一个库调用似乎解决了这个问题(作为副作用),但我看不出它在哪里关闭了任何流。再说一次,它可能只是触发一个运行终结器的 GC...
  • “再说一次,它可能只会触发运行终结器的 GC……”——这也是我的理论。但是有一些事情可以尝试看看是否是这种情况:用一个巨大的堆运行你的应用程序,这样 GC 就不会被堆耗尽触发。
  • 既然你发现了泄漏,我最后的评论有点多余。但如果你没有发现泄漏,它可能会有所帮助......所以我会把它留给后代。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-07
相关资源
最近更新 更多