在某些众所周知的情况下,我们的应用程序将打开过多的套接字(数据库连接)并达到操作系统允许的最大打开文件数。我们明白这一点;我们正在解决问题并提高限制。
我们无法解释的是为什么我们的应用程序的某些部分即使在连接数量减少并且我们完全在限制范围内之后也没有恢复。
在本例中,它是在 Tomcat 下运行的应用程序。
发生这种情况时,我们首先开始看到“打开的文件太多”错误:
SEVERE: Socket accept failed
java.net.SocketException: Too many open files
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:390)
at java.net.ServerSocket.implAccept(ServerSocket.java:453)
at java.net.ServerSocket.accept(ServerSocket.java:421)
at org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(DefaultServerSocketFactory.java:61)
at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:310)
at java.lang.Thread.run(Thread.java:619)
最终,我们开始NoClassDefFoundError
在尝试打开 HTTP 连接的应用程序线程中看到 s:
java.lang.NoClassDefFoundError: org/apache/commons/httpclient/protocol/ControllerThreadSocketFactory
at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:128)
at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)
at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$HttpConnectionAdapter.open(MultiThreadedHttpConnectionManager.java:1349)
[...]
Caused by: java.lang.ClassNotFoundException: org.apache.commons.httpclient.protocol.ControllerThreadSocketFactory
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1387)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1233)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 8 more
当错误的连接消失时,服务器再次开始接受连接,一切似乎都很好,但是我们留下了后一个错误,不断地被喷到 stderr。
尽管应用程序通常将卸载的类记录到标准输出,但在“打开的文件过多”错误之前、期间或之后,我看不到任何此类日志。
我最初的理论是,Hotspot JVM 在遇到“打开的文件太多”时会卸载看似未使用的类,但如果是这样,它不会记录这一事实。
编辑:正如 Stephen C 在下面指出的那样,如果它正在卸载类,并且在第一次重新加载时遇到错误,这可以解释为什么它永远不会恢复。我认为这是一个很好的工作理论。它是否记录在 Sun 文档中?为什么它不会像通常卸载类的方式那样记录正在卸载的类?
平台详情:
Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
Java HotSpot(TM) 64-Bit Server VM (build 14.0-b16, mixed mode)
Apache Tomcat Version 6.0.18