列出递归目录结构时,"系统中打开的文件太多"失败

Mic*_*Kay 7 java directory-structure

我已经实现了(在Java中)一个相当简单的迭代器来返回递归目录结构中文件的名称,并且在大约2300个文件之后它失败了"系统中打开的文件太多"(失败实际上是在尝试加载一个类,但我认为目录列表是罪魁祸首).

迭代器维护的数据结构是一个堆栈,其中包含在每个级别打开的目录的内容.

实际逻辑是相当基本的:

private static class DirectoryIterator implements Iterator<String> {

        private Stack<File[]> directories;
        private FilenameFilter filter;
        private Stack<Integer> positions = new Stack<Integer>();
        private boolean recurse;
        private String next = null;

        public DirectoryIterator(Stack<File[]> directories, boolean recurse, FilenameFilter filter) {
            this.directories = directories;
            this.recurse = recurse;
            this.filter = filter;
            positions.push(0);
            advance();
        }

        public boolean hasNext() {
            return next != null;
        }

        public String next() {
            String s = next;
            advance();
            return s;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

        private void advance() {
            if (directories.isEmpty()) {
                next = null;
            } else {
                File[] files = directories.peek();
                while (positions.peek() >= files.length) {
                    directories.pop();
                    positions.pop();
                    if (directories.isEmpty()) {
                        next = null;
                        return;
                    }
                    files = directories.peek();
                }
                File nextFile = files[positions.peek()];
                if (nextFile.isDirectory()) {
                    int p = positions.pop() + 1;
                    positions.push(p);
                    if (recurse) {
                        directories.push(nextFile.listFiles(filter));
                        positions.push(0);
                        advance();
                    } else {
                        advance();
                    }
                } else {
                    next = nextFile.toURI().toString();
                    count++;
                    if (count % 100 == 0) {
                        System.err.println(count + "  " + next);
                    }
                    int p = positions.pop() + 1;
                    positions.push(p);
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

我想了解这需要多少"打开文件".在什么情况下这个算法"打开"一个文件,什么时候再次关闭?

我看过一些使用Java 7或Java 8的简洁代码,但我受限于Java 6.

Mic*_*don 6

调用nextFile.listFiles()时,将打开基础文件描述符以读取目录.无法显式关闭此描述符,因此您依赖于垃圾回收.当你的代码下降到一棵深树时,它本质上是收集一堆nextFile实例,这些实例无法被收集.

步骤1:在调用advance()之前设置nextFile = null.这将释放垃圾回收对象.

第2步:在将nextFile归零后,您可能需要调用System.gc()以鼓励快速垃圾回收.不幸的是,没有办法强制GC.

第3步:您可能需要增加操作系统的打开文件限制.在Linux上,这可以使用ulimit(1)完成.

如果您可以迁移到Java 7或更高版本,则DirectoryStream将解决您的问题.而不是使用nextFile.listFiles(),使用Files.newDirectoryStream(nextFile.toPath())来获取DirectoryStream.然后,您可以遍历流,然后关闭()它以释放操作系统资源.可以使用toFile()将每个返回的路径转换回文件.但是,您可能希望重构仅使用Path而不是File.