java - 在 Tomcat 上部署为 WAR 的 Spring Boot 应用程序中增加类加载计数

标签 java performance spring-boot tomcat

我们有一个在 Tomcat 上运行的 Spring Boot 应用程序,它是一个 RESTful Web 服务。在我们的测试环境和生产环境中的 3 个 Tomcat 实例上部署了相同的 WAR 文件。在运行性能测试时,我们注意到某些服务器存在一个特殊问题。一些服务器在处理大约 2500 个请求后停止响应。该问题发生在 3 台生产服务器中的 2 台和 3 台测试服务器中的 1 台上。

在出现问题的服务器上,我们在 JVM 监控中注意到,每当我们运行性能测试时,加载的类计数都会不断增加。加载的类数从 20k 增加到大约 200 万。当类计数接近 200 万时,JVM 监控还显示 GC 花费的时间太长,超过 40 秒。一旦达到该点,应用程序将停止响应。应用程序抛出 OutOfMemoryException“压缩类空间”。如果我们继续发送更多请求,我们可以从应用程序日志中看到该服务仍在接收请求,但中途停止处理。

在没有问题的其他服务器上,类加载计数保持在 20k 不变。而且 GC 也很正常,用时不到 1 秒。

我们注意到的其他测试和行为 -

  1. 此问题发生在安装在 Windows PC 上的本地 Tomcat 实例上。服务器在 Linux 上。 OpenJDK 和 Oracle JDK 1.8 都会出现此问题。
  2. 我们验证了 Tomcat 实例彼此相同 - 我们甚至从工作服务器克隆并将它们放在坏服务器上。
  3. 使用不同的 GC 策略(PS、CMS 和 G1)进行了测试,所有这三个策略都出现了问题。
  4. 通过将应用程序作为独立的 Spring Boot JAR 运行进行测试,问题消失了。类计数保持不变,GC 行为正常。
  5. 该应用程序目前正在使用 JAXB 库执行 XML 编码/解码,我们在代码中找到了可以优化代码的地方。重构代码并迁移到 Jackson 库是另一种选择。

我的问题是 -

  • 当我们部署同一个 WAR 文件时,是什么导致了多个服务器之间的差异?
  • 是什么导致了作为部署在 Tomcat 上的 WAR 运行的应用程序与作为独立的 Spring boot 应用程序运行的应用程序之间的差异?
  • 如果我们对 JVM 进行堆转储或进行分析,需要注意哪些事项?

最佳答案

所以事实证明这是由于我们的类路径中的 jaxb 2.1 jar。感谢 Mark 指出 jaxb 的已知错误。

我们的应用程序没有明确地将 jaxb-impl 作为依赖项,因此一开始很难看出。查看 Maven 依赖关系树后,我们发现正在从其他项目和库加载两个不同的版本。我们的应用程序在类路径中有 jaxb-impl 版本 2.1 和 2.2.6。我们将 2.1 版本作为应用程序的 pom.xml 中的排除项并解决了该问题。

我的猜测是不同的服务器在应用程序启动时加载了不同的版本。这可能就是为什么有些服务器工作正常而其他加载 2.1 版本的服务器出现问题的原因。与作为独立的 Spring boot 应用程序运行类似,它可能加载了 2.1 版本。

关于java - 在 Tomcat 上部署为 WAR 的 Spring Boot 应用程序中增加类加载计数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58055998/

相关文章:

java - 扩展实现某些包含泛型的类的 ArrayList 不起作用?

java - 进程 fork 、子进程等[Java]

performance - 我如何通过归纳法证明这两种算法中的第二种算法更快?

c - f(n) = n + f(floor(n/2)) 是否有封闭形式?

java - 什么是 spring-boot-configuration-processor ?为什么人们将图书馆排除在外?为什么它在依赖树中不可见?

java - 如何解决生成的解析器中 ANTLR4 产生的编码问题?

java - Reader 不能*直接*从 StringBuilder 读取吗?

c++ - OpenCV:C++ 和 C 性能比较

java - 如何解析具有两种类型值的 JSON,一种是整数,另一种是 JSON 对象?

spring - 如何使用 @ConfigurationProperties 注入(inject) java.nio.file.Path 依赖项