Tomcat постепенно исчерпывает память с развернутыми приложениями WebSocket
У меня Tomcat 8.5.9, запущенный на AWS, с развернутыми 10 различными приложениями WebSocket, каждое из которых в основном действует как брокер сообщений. Коннектор https использует протокол Http11NioProtocol. Единственный параметр, который я установил, это maxThreads=200 вместе с информацией о сертификате.
Объем запросов не очень высок. Он работает с утра понедельника, и вот что говорит статус менеджера:
Макс темы: 200
Текущий счетчик потока: 38
Текущий поток занят: 0
Оставьте в живых количество сокетов: 1
Максимальное время обработки: 234 мс
Время обработки: 17.254 с
Количество запросов: 33351
Количество ошибок: 325
Получено байт: 0,00 МБ
Отправлено байт: 34,07 МБ
Через несколько дней я замечаю, что использование памяти продолжает расти. Я должен перезапускать сервисы Tomcat примерно каждые две недели, чтобы предотвратить получение исключения OutOfMemoryException.
Я принимал дампы кучи и анализировал с помощью Eclipse MAT, который всегда указывает на то, что класс WsFrameServer является подозреваемой проблемой. Самый последний дамп отображает следующее:
5 146 экземпляров "org.apache.tomcat.websocket.server.WsFrameServer",
загруженные "java.net.URLClassLoader @ 0x6c0047c28" занимают 1 383 143 200
(73,13%) байтов. На эти экземпляры ссылаются из одного экземпляра
"Java.util.concurrent.ConcurrentHashMap$Node[]"
Дерево Доминаторов в настоящее время содержит 106 000 записей, большинство из которых - класс WsFrameServer.
Я что-то не так делаю или это "нормально"? Есть ли какие-то особые настройки на Tomcat или Connector, которые я должен установить, чтобы этого не происходило?
Заранее спасибо.
РЕДАКТИРОВАТЬ: Я не уверен, если это полезно, но вот как выглядит монитор VisualVM:
3 ответа
Трудно быть уверенным без подробностей, но это, вероятно, связано с сохранением вашего сеанса. Я думаю, что происходит то, что WsFrameServer
который расширяется WsFrameBase
добавлен в сессию.
Если у вас есть неограниченная политика хранения сеансов, то в конечном итоге вам не хватит памяти.
Попробуйте установить не-0 sessionTimeout
Код отсутствует в вашем вопросе. (особенно, как вы управляете соединением через websocket)
Вы использовали tomcat в асинхронном режиме со списком соединений где-то?
Вы не забыли привязать событие ошибки AND к коду, удаляющему неисправное соединение из списка?
Как мы все знаем, Java GC ленива. Его память будет продолжать расти до тех пор, пока у нее не останется больше памяти, тогда будет запущен GC для сбора мусора.
На скриншоте вашей VisualVM видно, что использование памяти относительно нормальное: с течением времени используется больше памяти, после GC потребление памяти снижается.
Поэтому мне интересно, будет ли ваше приложение действительно зависать из-за OOM. Вы можете попробовать его в своей тестовой среде и проанализировать дамп JOM-файла OOM, что более полезно.
Кстати, я предлагаю VisualVM поверх MAT, потому что MAT будет включать некоторые недоступные объекты в качестве корня GC. Это сделает анализ памяти очень неэффективным и даст другой результат, чем другие инструменты, которые я встречал в одном из наших проектов.