当流未明确关闭时 Files.list(Path dir) 中的资源泄漏? [英] Resource leak in Files.list(Path dir) when stream is not explicitly closed?
问题描述
我最近编写了一个小应用程序,它会定期检查目录的内容.过了一会儿,由于打开的文件句柄太多,应用程序崩溃了.经过一些调试,我在以下行中发现了错误:
I recently wrote a small app that periodically checked the content of a directory. After a while, the app crashed because of too many open file handles. After some debugging, I found the error in the following line:
Files.list(Paths.get(destination)).forEach(path -> {
// To stuff
});
然后我检查了 Files.list
的 javadoc(我可能应该早点这样做)并发现:
I then checked the javadoc (I probably should have done that earlier) for Files.list
and found:
* <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
对我来说,及时处理"听起来仍然像是在应用程序退出之前最终会释放资源.我查看了 JDK (1.8.60) 代码,但找不到关于 Files.list
打开的文件句柄的任何提示再次被释放.
To me, "timely disposal" still sounds like the resources are going to be released eventually, before the app quits. I looked through the JDK (1.8.60) code but I wasn't able to find any hint about the file handles opened by Files.list
being released again.
然后我创建了一个小应用程序,它在使用 Files.list
后显式调用垃圾收集器,如下所示:
I then created a small app that explicitly calls the garbage collector after using Files.list
like this:
while (true) {
Files.list(Paths.get("/")).forEach(path -> {
System.out.println(path);
});
Thread.sleep(5000);
System.gc();
System.runFinalization();
}
当我用 lsof -p <pid>
检查打开的文件句柄时,我仍然可以看到/"的打开文件句柄列表越来越长.
When I checked the open file handles with lsof -p <pid>
I could still see the list of open file handles for "/" getting longer and longer.
我现在的问题是:在这种情况下,是否有任何隐藏机制最终应该关闭不再使用的打开文件句柄?或者这些资源实际上从来没有被处理过,javadoc在谈论及时处理文件系统资源"时有点委婉?
My question now is: Is there any hidden mechanism that should eventually close no longer used open file handles in this scenario? Or are these resources in fact never disposed and the javadoc is a bit euphemistic when talking about "timely disposal of file system resources"?
推荐答案
如果您关闭 Stream,Files.list()
确实会关闭它使用的底层 DirectoryStream
流式传输文件,所以只要关闭Stream就不会出现资源泄漏.
If you close the Stream, Files.list()
does close the underlying DirectoryStream
it uses to stream the files, so there should be no resource leak as long as you close the Stream.
您可以在此处查看 Files.list()
的源代码中 DirectoryStream
的关闭位置:
You can see where the DirectoryStream
is closed in the source code for Files.list()
here:
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false)
.onClose(asUncheckedRunnable(ds));
要理解的关键是 Runnable
使用 Stream::onClose
向流注册,当流本身关闭时调用该代码.该 Runnable 是由工厂方法创建的,asUncheckedRunnable
创建一个 Runnable
关闭传递给它的资源,翻译在 IOException
期间抛出的任何 IOException
code>close() 到 UncheckedIOException
The key thing to understand is that a Runnable
is registered with the Stream using Stream::onClose
that is called when the stream itself is closed. That Runnable is created by a factory method, asUncheckedRunnable
that creates a Runnable
that closes the resource passed into it, translating any IOException
thrown during the close()
to an UncheckedIOException
您可以通过确保 Stream
像这样关闭来安全地确保 DirectoryStream
已关闭:
You can safely assure that the DirectoryStream
is closed by ensuring the Stream
is closed like this:
try (Stream<Path> files = Files.list(Paths.get(destination))){
files.forEach(path -> {
// Do stuff
});
}
这篇关于当流未明确关闭时 Files.list(Path dir) 中的资源泄漏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!