繁体   English   中英

Java IOException“打开的文件太多”

[英]Java IOException "Too many open files"

我正在对多个文件进行一些文件 I/O(写入 19 个文件,它确实如此)。 在给他们写了几百次之后,我得到了 Java IOException : Too many open files 但实际上我一次只打开了几个文件。 这里有什么问题? 我可以验证写入是否成功。

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

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

你说你打开了 19 个文件,几百次后你得到一个 IOException 说“打开的文件太多”。 现在这个特殊的异常只会在请求一个新的文件描述符时发生; 即当您打开文件(或管道或套接字)时。 您可以通过打印 IOException 的堆栈跟踪来验证这一点。

除非您的应用程序以较小的资源限制运行(这似乎不太可能),否则它必须重复打开文件/套接字/管道,但无法关闭它们。 找出发生这种情况的原因,您应该能够弄清楚该怎么做。

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

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 正在尝试创建将连接到外部应用程序的标准输入/输出/错误的“管道”文件描述符。

对于 UNIX:

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

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

   $ ulimit -n

然后根据您的要求更改限制。

   $ ulimit -n <value>

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

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

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

该程序打开了一个 FileOutputStream (fos)、一个 BufferedOutputStream (bos) 和一个 DataOutputStream (dos)。 写入数据输出流后,dos 关闭,我认为一切正常。

然而,在内部,dos 试图刷新 bos,它返回磁盘已满错误。 该异常被 DataOutputStream 吃掉了,因此底层 bos 没有关闭,因此 fos 仍然打开。

在稍后阶段,该文件从(带有 .tmp 的东西)重命名为其真实名称。 因此,java 文件描述符跟踪器丢失了原始 .tmp 的跟踪,但它仍然是打开的!

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

我希望这可以帮助别人。

如果您在自动化测试中看到这一点:最好在测试运行之间正确关闭所有文件。

如果你不确定你打开了哪些文件,一个好的开始是抛出异常的“打开”调用! 😄

如果您有一个文件句柄,只要其父对象还活着,就应该打开它,您可以在父对象上添加一个finalize方法,该方法在文件句柄上调用 close。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM