问题描述

以下两个堆栈跟踪指示同一个问题并报告相同的消息:打开的文件过多

异常 1

java.net.SocketException: Too many open files
at java.net.PlainSocketImpl.accept(Compiled Code)
at java.net.ServerSocket.implAccept(Compiled Code)
at java.net.ServerSocket.accept(Compiled Code)
at weblogic.t3.srvr.ListenThread.run(Compiled Code)

异常 2

java.io.IOException:打开的文件过多
at java.lang.UNIXProcess.forkAndExec(Native Method)
at java.lang.UNIXProcess.(UNIXProcess.java:54)
at java.lang.UNIXProcess.forkAndExec(Native Method)
at java.lang.UNIXProcess.(UNIXProcess.java:54)
at java.lang.Runtime.execInternal(Native Method)
at java.lang.Runtime.exec(Runtime.java:551)
at java.lang.Runtime.exec(Runtime.java:477)
at java.lang.Runtime.exec(Runtime.java:443)

第一个异常在错误影响到基础 TCP 协议时抛出,而第二个异常则在错误影响到 I/O 操作时抛出。

这两个异常都是由阻止服务器的类似问题所产生的,该问题可通过下面的研究方法来解决。

为什么发生此问题?

这些异常指出操作系统 (OS) 资源问题和操作系统与 JVM 进程用完文件描述符的原因(请参阅什么是文件描述符?)

在几个并发用户连接到服务器之后通常会发生此问题。Java 打开许多文件,以便读取运行应用程序所必需的类。大量应用程序会使用许多文件描述符,这会导致缺乏新的文件描述符。同样,每个新的套接字都需要一个描述符。客户端和服务器通过 TCP 套接字进行通信。在与服务器建立连接时,每个浏览器的 http 请求都使用 TCP 套接字。

一定要首先监视文件描述符并了解这些诊断方法如何告知您有关打开文件的状态和其它潜在问题。在针对操作系统逐步执行此故障排除部分之后,可能有必要增加文件描述符的数量(请参阅文件描述符和设置)

监视文件描述符

解决方法指导原则

下面是一般指导原则和考虑事项:

确定文件描述符的总数是否太少或者某些文件描述符是否未被正确释放。

这可以通过以下方法来诊断:在不同的时期检查文件描述符的总数,确定此数量是有所减少还是一直增加。

如果此数量有所减少,则应当增加文件描述符的最大数量,以防止该问题再次发生(如何在不同平台上定义文件描述符的数量)。

此变化可以和减少连接在断开之前保持 TIME_WAIT 状态的时间结合起来(如何以及何时发布文件描述符?)。在繁忙的服务器上,缺省值(240 秒)会延迟其它连接企图,从而将限制最大连接数量。

如果此数量一直增加,则应当确定某些描述符的处理时间是否过长(文件没有正确地关闭 – 如何以及何时发布文件描述符?)以及所创建的文件是否过多(例如,驱动程序库一直为每个新的 JDBC 连接加载文件)。

加载 jar 文件还可能减少所使用的文件描述符的数量。每个 jar 文件都使用一个描述符,即使每个单独加载的单一类都将使用一个描述符时也是如此。

检查打开的文件

Unix 平台

在诸多工具中,lsof (LiSt Open Files) Unix 管理工具(适用于 Solaris、Tru64、HP-UX、Linux 和 AIX)显示有关打开文件和网络文件描述符的信息,包括它们的类型、大小和 i-节点。

对于特定的进程,其语法如下所示:

lsof -p 进程的pid;

示例 1

以下命令在 Solaris 2.7 启动 WLS 8.1SP1 后立即执行。它表明运行服务器的 Java 进程 (pid 390) 分配了 84 个文件描述符,此数量远小于文件描述符缺省的硬极限。

$ lsof -p 390 | wc -l

84

在异常出现之后可以执行此命令,以确保此 Java 进程达到了打开文件的最大数量。这将确认该进程缺乏文件描述符。

然后,您可以运行 $ lsof -p 并将输出结果重定向到某个文件以检查打开的每个文件。如果某个应当关闭的文件却出现在列表中,您可以探查此文件以前没有按照预期方式关闭的原因。

java.io.IOException: Too many open files异常的解决

在Java程序中使用Runtime.exec(” “)调用外部程序,得到ErrorStream和InputStream进行处理。

在使用一段时间之后,程序会报“打开文件数过多”的问题。

debug过程:

linux限制了一个程序打开的最大文件数,明显应该是超过了限制,这时有两种方法:

(1)重新设置最大打开文件数(Linux下使用ulimit);

(2) 检查程序本身存在的可能问题。

使用第一种在大多数情况下是治标不治本的,如果程序本身存在问题,比如使用了socket而没有关闭,那么会打开一个socket就占用一个file descriptor,随着时间的增长终会超出限制,并且存在隐患。

所以最好是先检查程序的问题。

Process process=Runtime.exec(” “);

process一般都是正常终止,本来以为process终止之后会自动释放资源,但是经过测试发现是没有的,如果程序一直执行Runtime.exec(),那么占用的file descriptor会不断增加,如果在程序中加入process.destroy(),则可以释放占用的file descriptor。

至于其他的由socket,或者调用其他外部命令产生的问题,我想首先还是从程序本身找原因,如果程序没有错误--比如它一下就需要创建很多进程或线程,同时打开了多个描述符,那么再调整系统参数--“打开的最大文件数”也不迟。