我用多个文件做一些文件I / O(写入19个文件,就这样发生了)。在给他们写了几百次后,我得到了Java IOException
:Too many open files
。但实际上我只有一些文件一次打开。这里有什么问题?我可以验证写入是否成功。
答案 0 :(得分:51)
在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正在尝试创建将连接到外部应用程序的标准输入/输出/错误的“管道”文件描述符。
答案 1 :(得分:3)
虽然在大多数情况下错误很明显,文件句柄还没有关闭,但我刚刚在Linux上遇到了一个JDK7的实例......这里有足够的解释来解释一下。
该程序打开了一个FileOutputStream(fos),一个BufferedOutputStream(bos)和一个DataOutputStream(dos)。写入dataoutputstream后,dos关闭了,我觉得一切都很顺利。
在内部,dos尝试刷新bos,返回Disk Full错误。这个例外被DataOutputStream吃掉了,结果底层的bos没有关闭,因此fos仍然是开放的。
在稍后阶段,该文件随后从(带有.tmp的东西)重命名为其真实姓名。因此,java文件描述符跟踪器丢失了对原始.tmp的跟踪,但它仍然是开放的!
要解决这个问题,我必须先自己刷新DataOutputStream,检索IOException并自己关闭FileOutputStream。
我希望这有助于某人。
答案 2 :(得分:2)
在打开新文件描述符之前,你显然没有关闭文件描述符。你在Windows或Linux上吗?
答案 3 :(得分:2)
对于UNIX:
正如Stephen C所建议的那样,将最大文件描述符值更改为更高的值可以避免此问题。
尝试查看您当前的文件描述符容量:
$ ulimit -n
然后根据您的要求更改限制。
$ ulimit -n <value>
请注意,这只会更改当前shell和任何子/后代进程的限制。要使更改“粘贴”,您需要将其放入相关的shell脚本或初始化文件中。
答案 4 :(得分:-7)
最近,我有一个程序批处理文件,我肯定在循环中关闭了每个文件,但错误仍然存在。
后来,我通过垃圾收集每一百个文件来解决这个问题:
int index;
while () {
try {
// do with outputStream...
} finally {
out.close();
}
if (index++ % 100 = 0)
System.gc();
}