Задача
Экземпляр Glassfish с 60 установленными веб-приложениями (.war) иногда сжигает 100-150% ядра ЦП или более в течение многих минут, в то время как для его оправдания нет значительной рабочей нагрузки.
Иногда становится так плохо, что приложения перестают отвечать, и экземпляр не завершает работу в разумные сроки (> 1 час) без уничтожения -9.
Окружающая среда
Linux (CentOS) 4 ядро, физическое оборудование.
Java 1.8u65, Oracle, Hotspot
Glassfish 4.1.1 - Установлено около 60 веб-приложений (военных файлов).
JMX сообщает о большом количестве свободной кучи (около 50%). Linux сообщает о незначительном давлении памяти.
Несущественные задачи GF, такие как автоматическое развертывание, автообновление, обновление, отключены.
Исследование
Я могу воспроизвести проблему в неактивной системе с la <= 0.3, перейдя к представлению «Приложения» в html-консоли администратора Glassfish после нового перезапуска GF со всеми приложениями, кроме отключенной консоли администратора. </p>
При переходе к представлению «Приложения» GF открывает 195 петлевых соединений ssl. Исходный код графического интерфейса администратора GF показывает, что их слой jsf будет отправлять> 1 https-запрос (самому себе) экземпляру GF для каждого установленного приложения.
Используя дампы потоков top / htop и JVM, я обнаружил, что все потоки с запущенным процессором были названы HandshakeCompletionNotify_Thread и созданы javax.net.SSLSocketImpl.
Многие потоки HandshakeCompletionNotify (> 40) зависали, и некоторые из них (от 1 до 4 на четырехъядерном экземпляре) всегда использовали от 40% до 100% ядра процессора.
Что говорит код?
Источник OpenJDK SSLSocketImpl предполагает, что HandshakeCompletionNotify_Thread действительно не имеет большого значения. Обычно он завершается очень быстро и завершает поток. Вот метод запуска из OpenJDK JDK 8, чтобы показать концепцию.
@Override
public void run() {
// Don't need to synchronize, as it only runs in one thread.
for (Map.Entry<HandshakeCompletedListener,AccessControlContext>
entry : targets) {
final HandshakeCompletedListener l = entry.getKey();
AccessControlContext acc = entry.getValue();
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
l.handshakeCompleted(event);
return null;
}
}, acc);
}
}
Мы не используем менеджер безопасности, поэтому doPrivileged не сможет существенно его задержать.
target - это HashSet <>, поэтому синхронизация отсутствует.
У обнаруженных мной реализаций HandshakeCompletedListener, похоже, нет причин блокировать метод handshakeCompleted ().
HandshakeCompletionNotify_Threads появляются в дампе потока в состоянии Runnable без трассировки стека. Не уверен, что это значит. Разве эти темы не реагировали на сигналы? (предположение) Разве они еще не вызвали метод run ()? Документация JDK предупреждает, что некоторые JVM могут оставлять кадры вне трассировки стека. Это объяснит это?
Вопрос
Почему эти рукопожатияCompletionNotify_Threads накапливаются и сжигают время процессора?
Как я могу предотвратить это?
Прогулки (ничего крепкого)
На самом деле мы ждем создания потока и инициализации. Там что-то заклинило.
Они записывают время в системном вызове, который я не вижу, возможно, выделяю или отображаю память.
Мы пропустили некоторые настройки для ssl, dns, что-то.
Почему меня это волнует?
Я провел расследование, чтобы выяснить, не было ли в программном обеспечении моей команды что-то слишком рискованное для выпуска в производственную среду. Теперь я уверен, что наше программное обеспечение не должно присутствовать, чтобы вызвать проблему.
Теперь эта проблема меня просто беспокоит, и я не хочу иметь проблемы с ней в масштабе. В JDK 1.6 сообщалось о нескольких ошибках JDK, связанных с этими потоками уведомлений о завершении, но они зарегистрированы как исправленные в JDK 7 и 8. Возможно, есть еще один аспект ошибки JDK. Возможно Glassfish или Grizzly имеет плохо реализованный HandShakeCompletionListener.