Увеличение количества загруженных классов в приложении Spring Boot, развернутом как WAR на Tomcat - PullRequest
1 голос
/ 23 сентября 2019

У нас есть приложение Spring Boot, работающее на Tomcat, это веб-сервис RESTful.Один и тот же файл WAR развернут на 3 экземплярах Tomcat в нашей тестовой среде, а также в производственной среде.Во время тестирования производительности мы заметили специфическую проблему с некоторыми серверами.Некоторые серверы перестают отвечать после обработки около 2500 запросов.Эта проблема возникает на 2 из 3 производственных серверов и на 1 из 3 тестовых серверов.

На серверах, на которых возникла проблема, мы заметили, что в нашем мониторинге JVM количество загружаемых классов продолжает увеличиваться всякий раз, когда мы выполняем тест производительности.Количество загруженных классов увеличивается с 20 000 до 2 миллионов.Когда количество классов достигает почти 2 миллионов, мониторинг JVM также показывает, что сборщик мусора занимает слишком много времени - более 40 секунд.Как только он достигнет этой точки, приложение перестанет отвечать на запросы.Приложения генерируют исключение OutOfMemoryException «Сжатое пространство классов».Если мы продолжим отправлять больше запросов, мы увидим из журналов приложений, что служба все еще получает запросы, но останавливает их обработку на полпути.

На других серверах без этой проблемы число загруженных классов остается на постоянном уровне 20 КБ.И GC тоже нормально, занимает не более 1 секунды.

Другие тесты и поведение, которые мы заметили -

  1. Проблема возникает на локальных экземплярах Tomcat, установленных на ПК с Windows.Серверы на Linux.Эта проблема возникает как в OpenJDK, так и в Oracle JDK 1.8.
  2. Мы убедились, что экземпляры Tomcat равны друг другу - мы даже клонировали с рабочих серверов и поместили их на плохие серверы.
  3. Протестированос разными политиками GC - PS, CMS и G1, и проблемы возникают на всех трех.
  4. Протестировано при запуске приложения в виде автономного Spring Boot JAR, и проблема исчезает.Количество классов остается постоянным, и GC ведет себя нормально.
  5. В настоящее время приложение использует библиотеки JAXB для выполнения маршалинга / демаршаллинга XML, и мы нашли в коде места, где мы можем оптимизировать код.Рефакторинг кода и переход в библиотеку Джексона - это еще один вариант.

Мои вопросы: *

  • Что может быть причиной различий между несколькими серверами при развертывании одной и той же WARфайл?.
  • Что может вызвать разницу между приложением, запущенным в качестве WAR, развернутым на Tomcat, и выполнением в качестве автономного загрузочного приложения Spring?
  • Если мы возьмем дамп кучи JVM или сделаемпрофилирование, на что обращать внимание?

1 Ответ

0 голосов
/ 28 сентября 2019

Получается, что это произошло из-за jaxb 2.1 jar в нашем classpath.Спасибо Марку за то, что он указал на известную ошибку с jaxb.

Наше приложение явно не имело jaxb-impl в качестве зависимости, поэтому поначалу было трудно это увидеть.Изучив дерево зависимостей Maven, мы обнаружили, что из другого проекта и библиотек загружаются две разные версии.Наше приложение имело jaxb-impl версии 2.1 и 2.2.6 в пути к классам.Мы поместили версию 2.1 в качестве исключения в pom.xml нашего приложения, и это решило проблему.

Я предполагаю, что разные серверы загружали разные версии при запуске приложения.Возможно, поэтому некоторые серверы работали нормально, а другие, которые загружали версию 2.1, имели проблемы.Аналогично запуску в качестве отдельного загрузочного приложения Spring, оно могло загрузить версию 2.1.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...