Java IOException“;“打开的文件太多”;

Java IOException“;“打开的文件太多”;,java,file-io,Java,File Io,我正在对多个文件进行一些文件I/O(写入19个文件,这种情况经常发生)。在给他们写了几百次之后,我得到了JavaIOException:打开的文件太多了。但实际上我一次只打开了几个文件。这里有什么问题?我可以验证写入是否成功。在打开新的文件描述符之前,您显然没有关闭文件描述符。您使用的是windows还是linux?在linux和其他类似UNIX/UNIX的平台上,操作系统对进程在任何给定时间可能具有的打开文件描述符的数量进行了限制。在过去,这一限制是硬规定的,而且相对较小。如今,它要大得多(成

我正在对多个文件进行一些文件I/O(写入19个文件,这种情况经常发生)。在给他们写了几百次之后,我得到了Java
IOException
打开的文件太多了。但实际上我一次只打开了几个文件。这里有什么问题?我可以验证写入是否成功。

在打开新的文件描述符之前,您显然没有关闭文件描述符。您使用的是windows还是linux?

在linux和其他类似UNIX/UNIX的平台上,操作系统对进程在任何给定时间可能具有的打开文件描述符的数量进行了限制。在过去,这一限制是硬规定的,而且相对较小。如今,它要大得多(成百上千),并且受制于每个进程可配置资源的“软”限制。(查找
ulimit
shell内置…)

Java应用程序必须超过每个进程文件描述符的限制

你说你打开了19个文件,几百次之后,你得到一个IOException,说“打开的文件太多了”。现在这个特殊的异常只有在请求新的文件描述符时才会发生;i、 e.打开文件(或管道或插座)时。您可以通过打印IOException的stacktrace来验证这一点

除非您的应用程序在资源限制很小的情况下运行(这似乎不太可能),否则它必须重复打开文件/套接字/管道,并且无法关闭它们。找出发生这种情况的原因,你就应该能够找到解决办法

仅供参考,以下模式是一种安全的文件写入方式,保证不会泄漏文件描述符

Writer w = new FileWriter(...);
try {
    // write stuff to the file
} finally {
    try {
        w.close();
    } catch (IOException ex) {
        // Log error writing file and bail out.
    }
}

1-硬连线,如编译到内核中。更改可用fd插槽的数量需要重新编译。。。并且可能导致用于其他事情的内存减少。在Unix通常在16位机器上运行的日子里,这些事情真的很重要

更新

Java 7方式更简洁:

try (Writer w = new FileWriter(...)) {
    // write stuff to the file
} // the `w` resource is automatically closed 

更新2


显然,在尝试运行外部程序时,您也可能会遇到“打开的文件太多”。基本原因如上所述。但是,在exec(…)
中遇到这种情况的原因是JVM试图创建“管道”文件描述符,该描述符将连接到外部应用程序的标准输入/输出/错误。

最近,我有一个程序批处理文件,我肯定已经关闭了循环中的每个文件,但错误仍然存在

后来,我通过对每数百个文件进行垃圾收集来解决这个问题:

int index;
while () {
    try {
        // do with outputStream...
    } finally {
        out.close();
    }
    if (index++ % 100 = 0)
        System.gc();
}
对于UNIX:

正如Stephen C所建议的,将最大文件描述符值更改为更高的值可以避免此问题

尝试查看您当前的文件描述符容量:

   $ ulimit -n
然后根据您的要求更改限制

   $ ulimit -n <value>
$ulimit-n

请注意,这只会更改当前shell和任何子进程/子进程中的限制。要使更改“保持不变”,您需要将其放入相关的shell脚本或初始化文件中。

尽管在大多数情况下,错误很明显是文件句柄尚未关闭,但我刚刚在Linux上遇到了一个JDK7实例。。。这里有足够的解释

该程序打开了一个FileOutputStream(fos)、一个BufferedOutputStream(bos)和一个DataOutputStream(dos)。在写入dataoutputstream之后,dos关闭了,我认为一切都很好

但是,在内部,dos试图刷新bos,这返回了一个磁盘已满错误。该异常被DataOutputStream吞噬,因此底层bos未关闭,因此fos仍处于打开状态

在后来的一个阶段,该文件被从(带有.tmp的东西)重命名为它的真实名称。因此,java文件描述符跟踪器失去了对原始.tmp的跟踪,但它仍然是开放的

为了解决这个问题,我必须首先自己刷新DataOutputStream,检索IOException并关闭FileOutputStream


我希望这对其他人有所帮助。

在将打开文件的函数中添加注释,并验证您只打开了每一个文件。您如何知道此错误是由于以下19种原因之一造成的?Java本身或您正在使用的框架可能正在打开文件。可能是在循环中打开这些文件,因此重复发生。如果您不自己关闭这些文件,让垃圾收集器为您清理,那么这将需要很长时间。与此同时,你正忙于打开更多的文件,不知不觉中,你已经达到了极限。如果他确实打开了19个文件,并且有一个包含文件描述符的数组,那么他应该很好,但我认为他实际上打开的文件比他想象的要多;例如,在我的XP SP3上,我可以毫不费力地遍历整个CXF(约15K)文件源代码树,而无需关闭任何打开的文件。我保证windows有文件描述符限制。它可能默认为65000对不起,但你错了。您可能认为您显式地关闭了所有流,但在程序的某个地方有一个执行路径,导致流未关闭。运行GC会导致“丢失”流被最终确定。流终结调用
this.close()
(IIRC)。很高兴知道gc()关闭流。@StefanReich它没有。它关闭文件描述符。溪流将保持不茂盛。我建议使用,我也会使用。但我不建议仅仅为了得到一个可以在2分钟内自己编写的方法而添加依赖项;i、 e.“Java 7路”。如果您仍在使用Java6或更早版本,则应升级。Java6已经发布了