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就不会有资源泄漏.
您可以DirectoryStream在Files.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)