You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tomcat反复热部署Spring Boot war包后出现Metaspace OOM问题

Spring Boot WAR包反复部署卸载导致Tomcat Metaspace内存溢出问题

问题概述

基于Spring Boot 2.7.5开发的简单应用,通过JPA访问Oracle 19c单表,打包为WAR包后在Tomcat 9.0.75上反复执行部署与卸载操作,多次后触发Metaspace OOM(元空间内存溢出),VisualVM监控显示平台期时Tomcat已出现挂起。

环境信息

  • OS:Ubuntu 22.04.3
  • JVM:Oracle Java 11.0.18 / Oracle Java 17.0.17(均验证过)
  • Tomcat:9.0.75
  • Spring Boot:2.7.5
  • Oracle:19c
  • Oracle JDBC:com.oracle.database.jdbc:ojdbc11:23.2.0.0

核心代码

Policy.java(实体类)

@Getter
@Setter
@Entity
@Table(name = "policy_m")
public class Policy {

    @Column(name = "policy_agreement_id")
    private Long policyAgreementId;
    @Id
    @Column(name = "policy_num")
    private String policyNum;
  
}

PolicyRepository.java(数据访问层)

@Repository
public interface PolicyRepository extends JpaRepository<Policy, String> {

    @Query(value = "select * from policy_m where policy_num = :policyNum", nativeQuery = true)
    Policy getPolicyByPolicyNum(@Param("policyNum") String policyNum);

}

PolicyServiceImpl.java(业务逻辑层)

@Service
public class PolicyServiceImpl implements PolicyService {

    @Autowired
    private PolicyRepository policyRepository;

    public Policy getPolicyByPolicyNum(String policyNum) {
        return policyRepository.getPolicyByPolicyNum(policyNum);
    }
}

DemoController.java(控制层)

@RestController
public class DemoController {

    @Autowired
    private PolicyService policyService;

    @GetMapping("/{policyNum}")
    public Policy database(@PathVariable("policyNum") String policyNum) {
        return policyService.getPolicyByPolicyNum(policyNum);
    }
}

复现步骤

  1. 打包生成应用WAR包
  2. 通过Tomcat管理控制台部署该WAR包
  3. 通过Tomcat管理控制台卸载该应用
  4. 重复执行步骤2-3,直至抛出Metaspace OOM异常

问题分析与解决方案

可能原因

Metaspace溢出本质是类加载器无法被GC回收,导致类元数据持续累积。结合场景,核心诱因包括:

  1. JDBC驱动类加载器泄漏:ojdbc11可能存在静态引用或未释放资源,导致Web应用类加载器无法被回收。
  2. Spring/JPA资源未彻底释放:Spring容器关闭时,JPA代理类、EntityManagerFactory等资源未完全清理,绑定到Web类加载器上。
  3. Tomcat类加载器机制问题:反复部署卸载时,Web应用类加载器未被正确回收,类元数据持续堆积在Metaspace。

解决方案

  1. 调整Tomcat类加载器配置
    在conf/context.xml中添加参数避免JAR锁定,确保类加载器可被回收:

    <Context antiResourceLocking="true" antiJARLocking="true">
        <!-- 原有配置 -->
    </Context>
    
  2. 优化JDBC驱动加载方式
    将ojdbc11依赖从WAR包中移除,放到Tomcat的lib目录下,由Tomcat共享类加载器加载,避免每个Web应用重复加载驱动类,减少Metaspace占用。

  3. 调整Metaspace内存参数
    在Tomcat的bin/catalina.sh(Linux)或catalina.bat(Windows)中添加JVM参数,临时缓解内存压力:

    JAVA_OPTS="$JAVA_OPTS -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
    
  4. 定位类加载器泄漏点
    使用jmap生成堆转储文件分析未回收的类加载器:

    jmap -dump:format=b,file=heapdump.hprof <Tomcat进程ID>
    

    检查是否存在第三方库静态缓存持有类加载器引用的情况,针对性清理资源。

内容的提问来源于stack exchange,提问作者Stephen Wong

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 20:57:26