似乎nio的.list
返回一个流,当使用该流时,每个迭代的文件保留一个文件描述符,直到在整个流上调用.close
。这意味着包含超过1,000个文件的数据目录可以轻松刷新常见的ulimit
值。在处理嵌套遍历时,此文件描述符累积的整体效果会进一步加剧。
除了生成对OS文件列表命令的调用之外,还有什么可能是迭代大目录文件的替代方法?如果迭代大目录的文件会很酷,只有当前迭代的文件才能维护文件描述符,正如适当的流语义所暗示的那样。
编辑:
list
返回java.nio.file.Path
的一个java流。一旦处理完,就会使用哪个api调用关闭流上的每个项目,而不是仅在整个流关闭时,更精简的迭代?在scala中,使用来自here的更好文件的api包装器可以很容易地摆弄它。
答案 0 :(得分:4)
如果发生这种情况,为什么不使用旧学校java.io.File?
File folder = new File(pathToFolder);
String[] files = folder.list();
使用lsof
进行测试,看起来列出的文件中没有一个是打开的。您可以在之后将数组转换为列表或流。除非目录太大或太远,否则我会尝试责怪Path对象并进行垃圾收集或以某种方式销毁它们。
答案 1 :(得分:2)
当我没有关闭流时,我遇到了同样的问题(在Windows Server 2012 R2上)。我迭代的所有文件都以读取模式打开,直到JVM关闭。但是,它在Mac OS X上没有出现,并且由于流依赖于FileSystemProvider
和DirectoryStream
的依赖于操作系统的实现,我认为该问题也可能与操作系统有关。
与@Ian McLaird的评论相反,Files.list()
文档中提到了
如果需要及时处理文件系统资源,则应使用try-with-resources构造来确保在流操作完成后调用流的close方法。
返回的流是DirectoryStream
,其Javadoc说:
DirectoryStream在创建时打开,并通过调用close方法关闭。关闭目录流会释放与该流关联的所有资源。无法关闭流可能会导致资源泄漏。
我的解决方案是遵循建议并使用try-with-resources
构造
try (Stream<Path> fileListing = Files.list(directoryPath)) {
// use the fileListing stream
}
当我正确关闭流(使用上面的try-with-resources
构造)时,文件句柄立即被释放。
如果您不关心将文件作为流文件,或者您可以将整个文件列表加载到内存中并自行将其转换为流,则可以使用IO API:
File directory = new File("/path/to/dir");
File[] files = directory.listFiles();
if (files != null) { // 'files' can be null if 'directory' "does not denote a directory, or if an I/O error occurs."
// use the 'files' array or convert to a stream:
Stream<File> fileStream = Arrays.stream(files);
}
我没有遇到任何文件锁定问题。但请注意,这两种解决方案都依赖于原生的,依赖于操作系统的代码,因此我建议您在所有使用的环境中进行测试。
答案 2 :(得分:1)
您可以使用使用旧版本的Apache FileUtils库 java.io.File.listFiles函数internaly:
Iterator<File> it = FileUtils.iterateFiles(folder, null, true);
while (it.hasNext())
{
File fileEntry = (File) it.next();
}