提问人:dpsthree 提问时间:11/27/2010 最后编辑:dpsthree 更新时间:1/20/2022 访问量:131976
Java IOException“打开的文件过多”
Java IOException "Too many open files"
问:
我正在对多个文件进行一些文件 I/O(写入 19 个文件,碰巧如此)。在给他们写了几百次之后,我得到了 Java : .但我实际上一次只打开了几个文件。这里有什么问题?我可以验证写入是否成功。IOException
Too many open files
答:
在打开新文件描述符之前,您显然没有关闭文件描述符。您使用的是 Windows 还是 Linux?
评论
在 Linux 和其他类 UNIX/UNIX 平台上,操作系统对进程在任何给定时间可能具有的打开文件描述符的数量进行了限制。在过去,这个限制曾经是硬连线的 1,而且相对较小。如今,它要大得多(数百/数千),并且受每个进程的“软”可配置资源限制的约束。(查找内置的 shell ...ulimit
您的 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
显然,在尝试运行外部程序时,您还会遇到“打开的文件过多”。基本原因如上所述。但是,您遇到这种情况的原因是 JVM 正在尝试创建“管道”文件描述符,这些描述符将连接到外部应用程序的标准输入/输出/错误。exec(...)
评论
"Deprecated. As of 2.6 removed without replacement. Please use the try-with-resources statement or handle suppressed exceptions manually."
close()
IOUtils
最近,我有一个程序批处理文件,我当然已经关闭了循环中的每个文件,但错误仍然存在。
后来,我通过每数百个文件热切地收集垃圾解决了这个问题:
int index;
while () {
try {
// do with outputStream...
} finally {
out.close();
}
if (index++ % 100 = 0)
System.gc();
}
评论
this.close()
对于 UNIX:
正如 Stephen C 所建议的,将最大文件描述符值更改为更高的值可以避免此问题。
尝试查看您当前的文件描述符容量:
$ ulimit -n
然后根据您的要求更改限制。
$ ulimit -n <value>
请注意,这只是更改了当前 shell 和任何子/后代进程中的限制。要使更改“坚持”,您需要将其放入相关的 shell 脚本或初始化文件中。
评论
尽管在大多数一般情况下,错误很明显是文件句柄尚未关闭,但我刚刚在 Linux 上遇到了一个带有 JDK7 的实例......足以在这里解释。
该程序打开了 FileOutputStream (fos)、BufferedOutputStream (bos) 和 DataOutputStream (dos)。写入 dataoutputstream 后,dos 被关闭,我认为一切正常。
但是,在内部,dos 尝试刷新 bos,这返回了磁盘已满错误。该异常被 DataOutputStream 吃掉,因此底层 bos 未关闭,因此 fos 仍处于打开状态。
在后来的阶段,该文件从(带有.tmp的东西)重命名为其真实名称。因此,java 文件描述符跟踪器失去了对原始 .tmp 的跟踪,但它仍然处于打开状态!
为了解决这个问题,我必须首先自己刷新 DataOutputStream,检索 IOException 并自己关闭 FileOutputStream。
我希望这对某人有所帮助。
评论
DataOutputStream
FilterOutputStream.close()
如果在自动化测试中看到此情况:最好在测试运行之间正确关闭所有文件。
如果您不确定您打开了哪些文件,那么一个好的起点是引发异常的“打开”调用!😄
如果文件句柄的父对象处于活动状态,则可以在父句柄上添加一个方法,该方法在文件句柄上调用 close。并在测试之间调用。finalize
System.gc()
评论