在O(1)打开的文件描述符中迭代scala / java中的文件

时间:2016-01-17 12:12:57

标签: java scala nio

似乎nio的.list返回一个流,当使用该流时,每个迭代的文件保留一个文件描述符,直到在整个流上调用.close。这意味着包含超过1,000个文件的数据目录可以轻松刷新常见的ulimit值。在处理嵌套遍历时,此文件描述符累积的整体效果会进一步加剧。

除了生成对OS文件列表命令的调用之外,还有什么可能是迭代大目录文件的替代方法?如果迭代大目录的文件会很酷,只有当前迭代的文件才能维护文件描述符,正如适当的流语义所暗示的那样。

编辑:

list返回java.nio.file.Path的一个java流。一旦处理完,就会使用哪个api调用关闭流上的每个项目,而不是仅在整个流关闭时,更精简的迭代?在scala中,使用来自here的更好文件的api包装器可以很容易地摆弄它。

3 个答案:

答案 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上没有出现,并且由于流依赖于FileSystemProviderDirectoryStream的依赖于操作系统的实现,我认为该问题也可能与操作系统有关。

与@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();
}