При определенных понятных обстоятельствах наше приложение откроет слишком много сокетов (соединений с базой данных) и достигнет максимально открытых файлов, которые позволяет ОС. Мы понимаем это; мы исправляем проблему, а также увеличиваем лимит.
То, что мы не можем объяснить, - это то, почему части нашего приложения не восстанавливаются даже после того, как число соединений уменьшится, и мы в пределах допустимого.
В данном случае это приложение, работающее под управлением 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
s внутри потока приложения, который пытается открыть HTTP-соединения:
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.
Хотя приложение обычно регистрирует выгруженные классы на стандартный вывод, я не вижу таких журналов непосредственно перед, во время или после ошибок «Слишком много открытых файлов».
Моя первоначальная теория заключалась в том, что JVM Hotspot будет выгружать, казалось бы, неиспользуемые классы, когда обнаружит «Слишком много открытых файлов», но если это так, это не регистрирует факт.
Редактировать: Как Стивен С указывает ниже, если он выгружает класс и сталкивается с ошибкой при первой перезагрузке, это может объяснить, почему он никогда не восстанавливается. Я думаю, что это хорошая рабочая теория. Это документировано в документации 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