【发布时间】:2016-08-27 16:01:18
【问题描述】:
我最近编写了一个定期检查目录内容的小应用程序。一段时间后,由于打开的文件句柄过多,应用程序崩溃了。经过一番调试,我发现了下面一行的错误:
Files.list(Paths.get(destination)).forEach(path -> {
// To stuff
});
然后我检查了 Files.list 的 javadoc(我可能早该这样做)并发现:
* <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 打开的文件句柄再次被释放的提示。
然后我创建了一个小应用程序,它在使用 Files.list 后显式调用垃圾收集器,如下所示:
while (true) {
Files.list(Paths.get("/")).forEach(path -> {
System.out.println(path);
});
Thread.sleep(5000);
System.gc();
System.runFinalization();
}
当我使用lsof -p <pid> 检查打开文件句柄时,我仍然可以看到“/”的打开文件句柄列表越来越长。
我现在的问题是:在这种情况下,是否有任何隐藏机制最终应该关闭不再使用的打开文件句柄?还是这些资源实际上从来没有被释放过,javadoc说的“及时释放文件系统资源”有点委婉?
【问题讨论】:
-
你是对的,如果你不关闭流,那么文件句柄将永远不会被关闭。
-
但是,如果您的进程退出,操作系统将在它之后进行清理。我怀疑“及时”是指“在流程结束之前”
-
可能是这样。尽管我发现您不应该像那样使用流并不是很直观。此外,IDE 似乎不会警告您这种潜在的资源泄漏,即使它应该很容易找到。
-
IDE 不会发出警告,因为几乎所有
Streams 都不需要关闭。理想情况下,Files.list()应该有一些 IDE 可以理解的注释,例如@CloseTheReturnedStream。 -
您的测试代码假定必须通过
finalize()执行自动清理,但通常现代代码使用基于PhantomReference的清理程序,这些清理程序不是由System.runFinalization()触发的。所以即使有自动清理,你也不会用你的代码检测到它。但是看the implementation 或its creator 你不会看到清洁工……