Java &引用;“系统中打开的文件太多”;列出递归目录结构时失败
我(在Java中)实现了一个相当简单的迭代器,以递归目录结构返回文件名,在大约2300个文件之后,“系统中有太多打开的文件”失败(失败实际上是在尝试加载类时,但我认为目录列表是罪魁祸首) 迭代器维护的数据结构是一个堆栈,其中包含在每个级别打开的目录的内容 实际逻辑相当基本:Java &引用;“系统中打开的文件太多”;列出递归目录结构时失败,java,directory-structure,Java,Directory Structure,我(在Java中)实现了一个相当简单的迭代器,以递归目录结构返回文件名,在大约2300个文件之后,“系统中有太多打开的文件”失败(失败实际上是在尝试加载类时,但我认为目录列表是罪魁祸首) 迭代器维护的数据结构是一个堆栈,其中包含在每个级别打开的目录的内容 实际逻辑相当基本: private static class DirectoryIterator implements Iterator<String> { private Stack<File[]>
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);
}
}
}
}
私有静态类DirectoryIterator实现迭代器{
私有堆栈目录;
私有文件名过滤器;
私有堆栈位置=新堆栈();
私有布尔递归;
私有字符串next=null;
公共目录迭代器(堆栈目录、布尔递归、文件名筛选器){
this.directories=目录;
this.recurse=recurse;
this.filter=过滤器;
位置。推(0);
前进();
}
公共布尔hasNext(){
返回下一步!=null;
}
公共字符串next(){
字符串s=下一个;
前进();
返回s;
}
公共空间删除(){
抛出新的UnsupportedOperationException();
}
私人预支{
if(directories.isEmpty()){
next=null;
}否则{
File[]files=目录.peek();
while(positions.peek()>=files.length){
目录pop();
positions.pop();
if(directories.isEmpty()){
next=null;
返回;
}
files=目录.peek();
}
File nextFile=files[positions.peek()];
if(nextFile.isDirectory()){
int p=positions.pop()+1;
位置:推(p);
if(递归){
directories.push(nextFile.listFiles(filter));
位置。推(0);
前进();
}否则{
前进();
}
}否则{
next=nextFile.toURI().toString();
计数++;
如果(计数%100==0){
System.err.println(计数+“”+下一步);
}
int p=positions.pop()+1;
位置:推(p);
}
}
}
}
我想了解这需要多少“打开的文件”。这种算法在什么情况下“打开”一个文件,什么时候会再次关闭
我见过一些使用Java 7或Java 8的简洁代码,但我只能使用Java 6。当调用nextFile.listFiles()时,会打开一个底层文件描述符来读取目录。无法显式关闭此描述符,因此您依赖于垃圾收集。当您的代码从一棵深树上往下看时,它实际上是在收集一堆不能随意收集的nextFile实例 步骤1:在调用advance()之前设置nextFile=null。这将释放用于垃圾收集的对象 步骤2:可能需要在为nextFile置零后调用System.gc(),以鼓励快速垃圾收集。不幸的是,没有办法强制GC 步骤3:您可能需要增加操作系统上的打开文件限制。在Linux上,这可以通过ulimit(1)完成
如果您可以迁移到Java 7或更高版本,那么DirectoryStream将解决您的问题。使用Files.newDirectoryStream(nextFile.toPath())来获取DirectoryStream,而不是使用nextFile.listFiles()。然后可以迭代流,然后关闭()以释放操作系统资源。每个返回的路径都可以用toFile()转换回文件。不过,您可能希望重构为只使用路径而不是文件。感谢大家的帮助和建议。我确定问题实际上在于迭代器返回文件后如何处理这些文件:“客户机”代码在文件交付时打开文件,并且没有正确整理。返回的文件实际上是并行处理的,这一事实使问题变得复杂 我还重写了DireectoryIterator,如果有人感兴趣,我将与大家分享:
private static class DirectoryIterator implements Iterator<String> {
private Stack<Iterator<File>> directories;
private FilenameFilter filter;
private boolean recurse;
private String next = null;
public DirectoryIterator(Stack<Iterator<File>> directories, boolean recurse, FilenameFilter filter) {
this.directories = directories;
this.recurse = recurse;
this.filter = filter;
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 {
Iterator<File> files = directories.peek();
while (!files.hasNext()) {
directories.pop();
if (directories.isEmpty()) {
next = null;
return;
}
files = directories.peek();
}
File nextFile = files.next();
if (nextFile.isDirectory()) {
if (recurse) {
directories.push(Arrays.asList(nextFile.listFiles(filter)).iterator());
}
advance();
} else {
next = nextFile.toURI().toString();
}
}
}
}
私有静态类DirectoryIterator实现迭代器{
私有堆栈目录;
私有文件名过滤器;
私有布尔递归;
私有字符串next=null;
公共目录迭代器(堆栈目录、布尔递归、文件名筛选器){
this.directories=目录;
this.recurse=recurse;
this.filter=过滤器;
前进();
}
公共布尔hasNext(){
返回下一步!=null;
}
公共字符串next(){
字符串s=下一个;
前进();
返回s;
}
公共空间删除(){
抛出新的UnsupportedOperationException();
}
私人预支{
if(directories.isEmpty()){
next=null;
}否则{
迭代器文件=目录.peek();
而(!files.hasNext()){
目录pop();
if(directories.isEmpty()){
next=null;
返回;
}
files=目录.peek();
}
File nextFile=files.next();
if(nextFile.isDirectory()){
if(递归){
目录.p