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

Fgb*_*nch 21 java file-io file out-of-memory

我必须处理一个大约200万xml的目录进行处理.

我已经解决了使用队列在机器和线程之间分配工作的处理,一切正常.

但现在最大的问题是用200万个文件读取目录的瓶颈,以便逐步填充队列.

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

aio*_*obe 11

首先,您是否有可能使用Java 7?在那里你有一个FileVisitor和它Files.walkFileTree,它应该在你的记忆约束中起作用.

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

或者,如果您控制文件的名称,或者它们以某种不错的方式命名,您可以使用在表单上接受文件名的过滤器以块的形式处理文件file0000000- filefile0001000然后file0001000- filefile0002000依此类推.

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


更新:叹息.可能不会起作用.刚看了一下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()]));
}
Run Code Online (Sandbox Code Playgroud)

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

顺便问一下,你能给出一个文件名的例子吗?他们是"可猜测的"吗?喜欢

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


Jör*_*ann 9

如果Java 7不是一个选项,那么这个hack将起作用(对于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);
}
Run Code Online (Sandbox Code Playgroud)

-f参数将加速它(from man ls):

-f     do not sort, enable -aU, disable -lst
Run Code Online (Sandbox Code Playgroud)


Mic*_*rdt 8

使用File.list()而不是File.listFiles()- String它返回的File对象比对象消耗更少的内存,并且(更重要的是,取决于目录的位置)它们不包含完整的路径名.

然后,File在处理结果时根据需要构造对象.

但是,这对于任意大的目录也不起作用.在目录层次结构中组织文件是一个总体上更好的想法,这样任何单个目录都不会有超过几千个条目.


Jai*_*zel 6

如果您可以使用Java 7,这可以通过这种方式完成,您将不会遇到内存不足问题.

Path path = FileSystems.getDefault().getPath("C:\\path\\with\\lots\\of\\files");
        Files.walkFileTree(path, new FileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                // here you have the files to process
                System.out.println(file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
               return FileVisitResult.TERMINATE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
              return FileVisitResult.CONTINUE;
            }
        });
Run Code Online (Sandbox Code Playgroud)