Подтвердить что ты не робот

Утечка памяти при перераспределении приложения в Tomcat

У меня есть WebApplication, который развернут в Tomcat 7.0.70. Я смоделировал следующую ситуацию:

  • Я создал кучу кучи.
  • Затем я отправил запрос Http и в методе службы я напечатал текущий поток и его классLoader. И затем я вызвал Thread.currentThread.sleep(10000).
  • И в тот же момент я нажал "undeploy это приложение" на странице администрирования Tomcat.
  • Я создал новую кучу кучи.
  • Через несколько минут я создал новый сброс hep.


Результаты


Дамп потока

На следующем экране вы можете увидеть, что после того, как я нажал "redeploy", все потоки (которые были связаны с этим веб-приложением) были убиты, за исключением потока "http-apr-8081-exec-10" . Когда я устанавливаю атрибут Tomcat "renewThreadsWhenStoppingContext == true", вы можете увидеть, что через некоторое время этот поток ( "http-apr-8081-exec-10" ) был убит и новый поток (http-apr-8081-exec-11 ) был создан вместо него. Поэтому я не ожидал наличия старого WCL после создания кучи dump 3, потому что нет старых потоков или объектов.

введите описание изображения здесь

Сброс Heapd 1

На следующих двух экранах вы можете видеть, что при запуске приложения существовал только один WCL (его параметр "начал" = true). И в потоке "http-apr-8081-exec-10" был contextClassLoader = URLClassLoader (потому что он был в пуле Tomcat). Я говорю только об этом потоке, потому что вы сможете увидеть, что этот поток будет обрабатывать мой будущий HTTP-запрос.

введите описание изображения здесь

введите описание изображения здесь

Отправка HTTP-запроса

Теперь я отправляю HTTP-запрос, и в своем коде я получаю информацию о текущем потоке. Вы можете видеть, что мой запрос обрабатывается потоком "http-apr-8081-exec-10"

дек 23, 2016 9:28:16 AM c.c.c.f.s.r.ReportGenerationServiceImpl INFO:  request has been handled in 
   thread = http-apr-8081-exec-10,  its contextClassLoader = WebappClassLoader
   context: /hdi
   delegate: false
   repositories:
   /WEB-INF/classes/
   ----------> Parent Classloader: [email protected]

Затем я нажимаю "Redeploy my web application", и я получаю следующее сообщение в консоли.

 дек 23, 2016 9:28:27 AM org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads
 SEVERE: The web application [/hdi] appears to have started a thread named [http-apr-8081-exec-10] but has failed to stop it. This is very likely to create a memory leak.

Heapd dump 2

На следующих экранах видно, что есть два экземпляра WebAppClassLoader. Один из них (номер # 1) является старым (его атрибут "начал" = ложь). И WCL # 2 был создан после повторного развертывания приложения (его атрибут "начал" = true). И поток, который мы рассмотрим, имеет contextClassLoader = "org.apache.catalina.loader.WebappClassLoader". Зачем? Я ожидал увидеть contextClassLoader = "java.net.URLClassLoader" (в конце концов, когда любой поток заканчивает свою работу, он возвращается в пул Tomcat и его атрибут "contextClassLoader" установлен в любой загрузчик базового класса).

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

Heapd dump 3

Вы можете видеть, что нет потока "http-apr-8081-exec-10" , но есть поток "http-apr-8081-exec-11", и он имеет contextClassLoader = "WebappClassLoader", (Почему бы не URLClassLoader?).

В итоге мы имеем следующее: есть поток "http-apr-8081-exec-11", который имеет ссылку на WebappClassLoader # 1. И obviosly, когда я делаю "Ближайший GC Root" на WCL # 1, я увижу ссылку ref в поток 11.

введите описание изображения здесь

введите описание изображения здесь

Вопросы.

Как я могу принудительно сказать Tomcat вернуть старое значение contextClassLoader (URLClassLoader) после того, как поток завершит свою работу?

Как я могу убедиться, что Tomcat не копирует старое значение "contextClassLoader" во время обновления потока?

Может быть, знаете ли вы другой способ решить мою проблему?

4b9b3361

Ответ 1

Tomcat обычно не является хорошим вариантом в производственных средах. Я использовал Tomcat для нескольких производственных приложений, и я обнаружил, что даже если размер кучи и другие конфигурации настроены правильно, и каждый раз, когда вы перезагружаете приложение, потребление памяти увеличивается и увеличивается. Пока вы не перезапустите службу tomcat, память не будет полностью восстановлена. Мы тестировали все такие эксперименты, такие как очистка журналов, передислокация всех приложений, регулярное перезапуск tomcat один раз в месяц или неделю в течение менее загруженных часов. Но в конце я должен сказать, что мы переместили нашу производственную среду на Glassfish и WebSphere.

Надеюсь, вы уже прошли эти страницы:

Утечка памяти в веб-приложении Java

Утечка памяти Tomcat Fix?

https://developers.redhat.com/blog/2014/08/14/find-fix-memory-leaks-java-application/

http://www.tomcatexpert.com/blog/2010/04/06/tomcats-new-memory-leak-prevention-and-detection

Если ваши веб-приложения не тесно связаны с Tomcat, тогда вы можете подумать об использовании другого веб-контейнера. Теперь мы используем Glassfish даже на машинах и производстве разработки, и в тот день, когда мы принимаем это решение, мы сэкономили много времени. Хотя Glassfish и другой такой сервер занимают больше времени, пока они начинаются, поскольку они не так легки, как Tomcat, но после того, как жизнь немного легче.

Ответ 2

Из моего опыта с этой проблемой, что мешало tomcat корректно загружать старшие классы класса GC, было несколько ThreadLocal, которые использовались в нескольких фреймворках, которые я использовал, создавали (и не правильно обрабатывали).

Нечто похожее на то, что объясняется здесь: ThreadLocal и утечка памяти

Я попытался правильно завершить этот ThreadLocal, и моя утечка уменьшилась LOT. Он все еще протекал, но я мог обрабатывать в 10 раз больше перераспределителей, чем раньше.

Я бы определенно проверил ваши дампы памяти на объекты, которые каким-то образом могут быть связаны с ThreadLocal (они очень распространены, особенно если вы используете что-то для управления транзакциями или что-то, что изолировано от потока).

Надеюсь, это поможет!

Ответ 3

Утечка памяти при повторной установке tomcat - очень старая проблема. Единственный реальный способ решить это - перезапустить tomcat вместо повторного развертывания приложения. Если у вас есть несколько приложений, вам нужно запустить несколько сервисов tomcat на разных портах и ​​присоединиться к нему с помощью nginx.

Ответ 4

У нас есть сотни экземпляров Tomcat, работающих в нескольких средах (также производство), и единственным разумным решением, которое мы обнаружили в этой проблеме, является остановка и перезапуск каждого Tomcat в заданное время ежедневно (в ночное время).

Мы пробовали много трюков, но это прочное решение для наших требований времени безотказной работы.

Ответ 5

Tomcat обычно не является хорошим вариантом в производственных средах. Я использовал Tomcat для нескольких производственных приложений, и я обнаружил, что даже если размер кучи и другие конфигурации настроены правильно, и каждый раз, когда вы перезагружаете приложение, потребление памяти увеличивается и увеличивается. Пока вы не перезапустите службу tomcat, память не будет полностью восстановлена. Мы тестировали все такие эксперименты, такие как очистка журналов, передислокация всех приложений, регулярное перезапуск tomcat один раз в месяц или неделю в течение менее загруженных часов. Но в конце я должен сказать, что мы изменили нашу производственную среду на Glassfish и WebSphere.

Ответ 6

Проверьте использование ThreadLocal, которое предотвратит сбор мусора для ClassLoader. Либо удалите ссылки на свои классы в значениях ThreadLocal, либо используйте https://github.com/codesinthedark/ImprovedThreadLocal вместо ThreadLocal