如何在java中列出200万个文件目录而不会出现“内存不足”异常

2022-09-02 00:54:27

我必须处理一个大约200万个要处理的xml的目录。

我已经解决了使用队列在机器和线程之间分配工作的处理,一切都很顺利。

但现在最大的问题是读取包含200万个文件的目录的瓶颈,以便增量填充队列。

我尝试过使用这种方法,但它给了我一个java异常。有什么想法吗?File.listFiles()out of memory: heap space


答案 1

首先,你有没有可能使用Java 7?在那里,您有 一个 和 ,它们可能应该在您的内存约束内工作。FileVisitorFiles.walkFileTree

否则,我能想到的唯一方法是将File.listFiles(FileFilter过滤器)与始终返回false(确保整个文件数组永远不会保存在内存中)的过滤器一起使用,但该过滤器捕获要在此过程中处理的文件,并可能将它们放在生产者/消费者队列中或将文件名写入磁盘以供以后遍历。

或者,如果您控制文件的名称,或者如果它们以某种很好的方式命名,则可以使用接受表单上的文件名的过滤器(然后-等)按块处理文件。file0000000filefile0001000file0001000filefile0002000

如果名称不是以这样好的方式命名的,您可以尝试根据文件名的哈希代码过滤它们,这应该在整数集上相当均匀地分布。


更新:叹息。可能不起作用。刚刚看了一下 listFiles 的实现:

public File[] listFiles(FilenameFilter filter) {
    String ss[] = list();
    if (ss == null) return null;
    ArrayList v = new ArrayList();
    for (int i = 0 ; i < ss.length ; i++) {
        if ((filter == null) || filter.accept(this, ss[i])) {
            v.add(new File(ss[i], this));
        }
    }
    return (File[])(v.toArray(new File[v.size()]));
}

所以无论如何,它可能会在第一行失败...有点令人失望。我相信你最好的选择是把文件放在不同的目录中。

顺便说一句,你能举一个文件名的例子吗?它们是“可猜测的”吗?喜欢

for (int i = 0; i < 100000; i++)
    tryToOpen(String.format("file%05d", i))

答案 2

如果Java 7不是一个选项,这个黑客将起作用(对于UNIX):

Process process = Runtime.getRuntime().exec(new String[]{"ls", "-f", "/path"});
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while (null != (line = reader.readLine())) {
    if (line.startsWith("."))
        continue;
    System.out.println(line);
}

-f 参数将加快速度(从):man ls

-f     do not sort, enable -aU, disable -lst