当流未明确关闭时,Files.list(Path dir)中的资源泄漏?

Jan*_*sen 22 java stream java-8

我最近写了一个小应用程序,定期检查目录的内容.过了一会儿,应用程序因为打开文件句柄太多而崩溃.经过一些调试,我发现以下行中的错误:

Files.list(Paths.get(destination)).forEach(path -> {
     // To stuff
});
Run Code Online (Sandbox Code Playgroud)

然后我检查了javadoc(我可能应该早点完成)Files.list并找到:

* <p> The returned stream encapsulates a {@link DirectoryStream}.
* If timely disposal of file system resources is required, the
* {@code try}-with-resources construct should be used to ensure that the
* stream's {@link Stream#close close} method is invoked after the stream
* operations are completed
Run Code Online (Sandbox Code Playgroud)

对我来说,"及时处理"仍然听起来资源将最终在应用程序退出之前发布.我查看了JDK(1.8.60)代码,但是我无法找到任何关于通过Files.list再次发布打开的文件句柄的提示.

然后我创建了一个小应用程序,在使用后显式调用垃圾收集器Files.list:

while (true) {
    Files.list(Paths.get("/")).forEach(path -> {
      System.out.println(path);
    });
    Thread.sleep(5000);

    System.gc();
    System.runFinalization();
}
Run Code Online (Sandbox Code Playgroud)

当我检查打开的文件句柄时,lsof -p <pid>我仍然可以看到"/"的打开文件句柄列表变得越来越长.

我现在的问题是:在这种情况下,是否有任何隐藏机制最终应该关闭不再使用的打开文件句柄?或者这些资源实际上从未被处理过,而且当谈到"及时处理文件系统资源"时,javadoc有点委婉?

Han*_*k D 21

如果关闭Stream,Files.list()会关闭DirectoryStream它用于流式传输文件的底层,因此只要关闭Stream就不会有资源泄漏.

您可以DirectoryStreamFiles.list()此处查看源代码中关闭的位置:

return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false)
                    .onClose(asUncheckedRunnable(ds));
Run Code Online (Sandbox Code Playgroud)

要理解的关键是Runnable使用Stream::onClose在流本身关闭时调用的a.Runnable是由一个工厂方法asUncheckedRunnable创建的,它创建一个Runnable关闭传递给它的资源,将任何IOException抛出的资源转换close()UncheckedIOException

您可以DirectoryStream通过确保Stream关闭这样关闭来确保关闭:

try (Stream<Path> files = Files.list(Paths.get(destination))){
    files.forEach(path -> {
         // Do stuff
    });
}
Run Code Online (Sandbox Code Playgroud)

  • 你救了我的一天! (2认同)
  • @Simrankaur,这是 try_with_resources 的一个例子。在 `try` 之后的括号内声明的变量将自动关闭,无论 try 块是正常完成还是异常完成。 (2认同)