【问题标题】:Java keeps lock on files for no apparent reasonJava 无缘无故地锁定文件
【发布时间】:2009-02-25 16:51:52
【问题描述】:

尽管在 finally 子句中关闭了流,但在使用 Java 时,我似乎经常遇到清理问题。 File.delete() 无法删除文件,Windows 资源管理器也失败。运行 System.gc() 有时会有所帮助,但终止 VM 会有所帮助,这不是一种选择。

有没有人有我可以尝试的其他想法?我在 Windows XP 上使用 Java 1.6。

更新:删除了 FLAC 代码示例,如果我将其隔离,代码就可以工作。

更新: 更多信息,这发生在 Apache Tomcat 中,Commons FileUpload 用于上传文件并且可能是罪魁祸首,我也使用 Runtime.exec() 在单独的进程中执行 LAME 来对文件进行编码,但这似乎不太可能导致这个因为 ProcessExplorer 清楚地表明 java.exe 在文件上有一个 RW 锁,并且 LAME 可以正常终止。

更新:我正在假设缺少 close() 或 close() 不会在我的代码或外部库中的某处被调用。我就是找不到!

【问题讨论】:

  • 我们需要更多信息才能为您提供帮助。你是怎么打开文件的?您是否绝对肯定要关闭每个文件,并且文件是否有可能没有关闭?删除时,您是否检查布尔返回统计信息以了解它是否失败,以及在哪个文件上?
  • 抱歉信息不足,现在添加代码示例。
  • 现在您已经删除了示例代码,大多数答案都没有意义......
  • 它们确实有道理。在我的代码或外部库中的某个地方缺少 close(),因为在运行垃圾回收之前问题仍然存在。然后运行 ​​FileOutputStream 的 finalize() 并关闭文件并可以删除。
  • 我怀疑是否存在 Java 中的错误。

标签: java file locking


【解决方案1】:

您发布的代码看起来不错 - 它不会导致您所描述的问题。我知道您只发布了您拥有的一段代码 - 您可以尝试仅将这部分提取到一个单独的程序中,运行它并查看问题是否仍然存在? 我的猜测是代码中还有其他地方可以执行new FileInputStream(path); 并且没有正确关闭流。当您尝试删除文件时,您可能只是在此处看到结果。

【讨论】:

  • 我现在使用了 ProcessExplorer,它清楚地表明 java.exe 有一个带有共享标志 RW 的句柄。这是否意味着我应该寻找未关闭的 FileOutputStreams?
  • 很难猜出你对这些文件做了什么 :-) 但我的猜测是通过代码并找到你可能阅读的所有地方应该不会太难/write 该文件并确保流已关闭。
【解决方案2】:

我假设您使用的是jFlac。我下载了 jFlac 1.3 并在从 Internet 现场音乐档案新下载的 flac 上尝试了您的示例代码。对我来说,它奏效了。我什至用ProcessExplorer 监视它,看到文件句柄被打开然后释放。你的测试代码真的和你给我们的一样简单,还是你的代码的简化版本?对我来说,一旦调用 close(),句柄就被释放,文件随后被成功删除。

尝试将无限循环更改为:

File toDelete = new File(path);
if (!toDelete.delete()) {
  System.out.println("Could not delete " + path);
  System.out.println("Does it exist? " + toDelete.exists());
}

或者如果您想继续循环,则在尝试删除文件之间放置 1 秒睡眠。我在 WinXP Pro 上用 JDK6 试过这个。

不要忘记在 close() 周围加上 try/catch 并在关闭引发异常时记录错误。

【讨论】:

  • 谢谢 Eddie,我也会做一个更简单的测试用例然后回来。
  • 你是对的,代码不是原因,锁是在我的应用程序的其他地方创建的。正在搜索...
【解决方案3】:

确保您在 finally 块中而不是在 try 块中进行密切调用。如果由于方法抛出异常而没有 try/finally,则添加 try/finally 并将 close 放在那里。

查看 Windows 任务管理器。对于进程,添加“句柄”列(在“视图”菜单下)。观察手柄是否不断上升而不会掉落。

使用分析器查看您周围是否有您认为不应该拥有的 Stream/Reader/Writer 对象。

编辑:

感谢您发布代码...关闭查看。有一件事 - 你的 close 方法不能保证执行 - 第一个 close 可能会抛出,然后第二个不会运行。

编辑2:

最终的 WavWriter wavWriter = new WavWriter(os); LACDecoder 解码器 = new FLACDecoder(is);

上面两行可能会导致 strams 被保存在实例变量中。作为测试,看看您是否可以在调用 decoder.decode() 之后将流引用设置为 null(也许可以创建一个 decoder.cleanup() 方法)。看看坚持关闭的流是否会导致问题。

另外,您是否对传递给上述构造函数的流进行任何包装?如果是这样,您可能必须通过包装器关闭流。

【讨论】:

  • (实际上 FileInputSteram.close 不会抛出,尽管这不是借口。)
  • 将这些变量设置为 null 应该没有任何区别。
  • 如果他以某种方式保留一些其他对象,这反过来又导致流被保存,那将会有所不同。虽然一旦流关闭,我看不出它为什么会保持文件锁定。我怀疑他有某种资源泄漏。
【解决方案4】:

您的代码示例绝对可以工作。事实上,我使用 jflac 1.3 在 Java 1.6/Vista 上运行了它,并且源文件被删除,没有任何循环。

我猜你的情况是另一个进程保持文件打开,可能是桌面搜索索引器或防病毒软件。您可以procexp 查找实际持有文件的进程。

【讨论】:

  • 我现在使用了 ProcessExplorer,它清楚地表明 java.exe 有一个带有共享标志 RW 的句柄。
  • 由于 ProcessExplorer 显示文件已打开以进行写入,我会说它没有被 commons-fileupload 正确关闭。也许您可以发布使用 commons-fileupload 的代码?
  • 是的,但我就是找不到 finally 块? FileUpload 似乎做对了……
【解决方案5】:

这不是一个空的while循环吗?

你有:

try
{
...code
}
finally
{
}
while (something);

在里面放一些空格,你实际上有:

try
{
...code
}
finally
{
}


while (something)
   ;

您的 while 循环与您的 try/finally 无关。如果您最初的 try 语句失败并且没有创建文件,则该 while 循环将永远不会完成,因为 try/finally 永远不会执行第二次。

您是否打算将其设为 do{ all your code } while(您的 while 语句)? 因为那不是你所拥有的。

编辑澄清: 我的建议是更改您的 while 循环以获取有关它无法删除的原因的更多信息:

while (!file.delete())
{
    if (!file.exists())
        break; // the file doesn't even exist, of course delete will fail

    if (!file.canRead())
        break; // the file isn't readable, delete will fail

    if (!file.canWrite())
        break; // the file isn't writable, delete will fail
}

因为如果删除失败一次,它就会一遍又一遍地失败,当然它会挂在那里。您没有在循环中更改文件的状态。

现在您已经添加了其他信息,例如 Tomcat 等,这是权限问题吗?您是否正在尝试写入用户 tomcat 正在运行的文件(没有人?) vm 无法创建?还是删除tomcat进程无法删除的文件?

如果进程资源管理器/etc 说 java 锁定了文件,那么某些东西仍然有一个打开的流使用它。有人可能没有在写入文件的任何流上正确调用 close()?

【讨论】:

  • while 循环不是空的,确切地说。它说,当文件没有被成功删除时,请再次尝试删除它。
  • 是的,你的 while 不是“空的”,但你的 while 是“愚蠢的”。它一遍又一遍地检查完全相同的状态,而while循环的内部没有做任何改变它。您希望操作系统告诉您多少次“您无法删除该文件”而不输入一些代码来找出原因?
【解决方案6】:

如果你没有线索和想法:在 cygwin 中,cd 到你的 javaroot 并运行类似的东西:

find . -name '*.java' -print0 | xargs -0 grep "new.*new.*putStream"

它可能会提供一些嫌疑人......

【讨论】:

    【解决方案7】:

    在您使用 Tomcat 后要尝试的另一件事 - 在您的上下文描述符(通常是 Tomcat/conf/Catalina/localhost/your-context.xml)中,您可以设置 antiResourceLocking=true,旨在“避免 Windows 上的资源锁定”。默认情况下(如果您未指定)为 false。值得一试。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-13
      • 1970-01-01
      • 1970-01-01
      • 2020-03-06
      • 1970-01-01
      • 2013-08-09
      • 2013-08-11
      • 1970-01-01
      相关资源
      最近更新 更多