当流未明确关闭时 Files.list(Path dir) 中的资源泄漏? [英] Resource leak in Files.list(Path dir) when stream is not explicitly closed?

查看:34
本文介绍了当流未明确关闭时 Files.list(Path dir) 中的资源泄漏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 期间抛出的任何 IOExceptioncode>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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆