Wildfly 11.0.0 Final部署后出现java.lang.OutOfMemoryError: Metaspace错误
Metaspace溢出问题解决方案
核心根因
- Wildfly模块化类加载机制与Spring Boot类加载逻辑冲突:独立jar包运行时使用Spring Boot嵌入式容器的类加载隔离机制,无类持有问题;迁移到Wildfly后,调度任务线程未绑定当前应用的类加载器,持续持有类加载器引用,导致加载的类无法被GC,Metaspace占用持续上涨最终溢出
- Spring Boot 2.0.2与Wildfly 11存在JTA事务兼容性问题:Spring Boot自带的事务实现与Wildfly容器级
org.jboss.jts模块冲突,每次调度任务执行都会重复生成事务代理类,无法被卸载 - 自定义Executor线程池未做类加载器配置:默认初始化的线程池会继承Wildfly全局类加载器,执行任务时加载的类都会被全局加载器持有,永远无法被GC
分步修复方案
第一步:临时调整JVM参数快速止血
修改Wildfly启动配置文件(Windows为standalone.conf.bat,Linux为standalone.conf),在JAVA_OPTS配置段新增以下参数:
# JDK8 版本参数 -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1024m -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled # JDK11+ 版本替换CMS相关参数为 -XX:+ClassUnloadingWithConcurrentMark
调整后重启Wildfly,可先避免服务频繁宕机,为后续修复争取时间。
第二步:修复Spring Boot与Wildfly兼容性配置
- 所有war包的pom.xml文件中,将嵌入式容器、JTA相关依赖设置为
provided,交由Wildfly容器提供,避免依赖冲突重复加载类:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jta-narayana</artifactId> <scope>provided</scope> </dependency>
- Spring Boot启动类继承
SpringBootServletInitializer,重写configure方法,同时添加@EnableTransactionManagement(proxyTargetClass = true)注解,强制使用CGLIB代理,减少动态代理类重复生成。 - 调度线程池手动绑定应用类加载器,初始化线程池时添加以下配置:
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(5); // 核心配置:将线程上下文类加载器设置为当前应用的类加载器 executor.setThreadFactory(runnable -> { Thread thread = new Thread(runnable); thread.setContextClassLoader(this.getClass().getClassLoader()); return thread; }); executor.initialize();
如果仍使用@Scheduled注解,直接配置全局调度线程池的类加载器即可,无需完全替换为自定义Executor。
第三步:验证修复效果
执行以下命令监控Metaspace使用情况:jstat -gc <进程PID> 1000 10
观察MC(Metaspace总容量)、MU(Metaspace已使用容量)数值,若Full GC后MU数值明显回落,说明类加载泄漏问题已修复。
如果调整后问题仍存在,可将携带调度任务的war包单独部署到独立的Wildfly实例,与其他3个war包做物理隔离,避免类加载互相影响。
内容的提问来源于stack exchange,提问作者Wit Wikky
相关产品推荐
相关产品推荐

