У нас есть приложение Spring Boot, работающее на Tomcat, это веб-сервис RESTful.Один и тот же файл WAR развернут на 3 экземплярах Tomcat в нашей тестовой среде, а также в производственной среде.Во время тестирования производительности мы заметили специфическую проблему с некоторыми серверами.Некоторые серверы перестают отвечать после обработки около 2500 запросов.Эта проблема возникает на 2 из 3 производственных серверов и на 1 из 3 тестовых серверов.
На серверах, на которых возникла проблема, мы заметили, что в нашем мониторинге JVM количество загружаемых классов продолжает увеличиваться всякий раз, когда мы выполняем тест производительности.Количество загруженных классов увеличивается с 20 000 до 2 миллионов.Когда количество классов достигает почти 2 миллионов, мониторинг JVM также показывает, что сборщик мусора занимает слишком много времени - более 40 секунд.Как только он достигнет этой точки, приложение перестанет отвечать на запросы.Приложения генерируют исключение OutOfMemoryException «Сжатое пространство классов».Если мы продолжим отправлять больше запросов, мы увидим из журналов приложений, что служба все еще получает запросы, но останавливает их обработку на полпути.
На других серверах без этой проблемы число загруженных классов остается на постоянном уровне 20 КБ.И GC тоже нормально, занимает не более 1 секунды.
Другие тесты и поведение, которые мы заметили -
- Проблема возникает на локальных экземплярах Tomcat, установленных на ПК с Windows.Серверы на Linux.Эта проблема возникает как в OpenJDK, так и в Oracle JDK 1.8.
- Мы убедились, что экземпляры Tomcat равны друг другу - мы даже клонировали с рабочих серверов и поместили их на плохие серверы.
- Протестированос разными политиками GC - PS, CMS и G1, и проблемы возникают на всех трех.
- Протестировано при запуске приложения в виде автономного Spring Boot JAR, и проблема исчезает.Количество классов остается постоянным, и GC ведет себя нормально.
- В настоящее время приложение использует библиотеки JAXB для выполнения маршалинга / демаршаллинга XML, и мы нашли в коде места, где мы можем оптимизировать код.Рефакторинг кода и переход в библиотеку Джексона - это еще один вариант.
Мои вопросы: *
- Что может быть причиной различий между несколькими серверами при развертывании одной и той же WARфайл?.
- Что может вызвать разницу между приложением, запущенным в качестве WAR, развернутым на Tomcat, и выполнением в качестве автономного загрузочного приложения Spring?
- Если мы возьмем дамп кучи JVM или сделаемпрофилирование, на что обращать внимание?