Tomcat 10关闭时报Log4j 2 ThreadLocal内存泄漏错误该如何解决?
问题根因
- 依赖适配不兼容:Tomcat 10 属于Jakarta EE 9及以上版本,全面使用
jakarta.servlet.*包替代了旧的javax.servlet.*包,你使用的Log4j 2.14.1版本的log4j-web依赖仅支持javax.servlet规范,无法在Tomcat 10环境中正常注册上下文监听器,导致应用关闭时Log4j不会主动清理内部持有的ThreadLocal资源。你之前尝试把jakarta.servlet相关jar放到tomcat/lib目录无效,就是因为问题并非缺少Servlet包,而是Log4j的web适配层不支持Jakarta Servlet规范导致的。 - Log4j2默认开启线程本地缓存优化:为了提升日志写入性能,Log4j2默认会用ThreadLocal缓存
MutableLogEvent、ReusableSimpleMessage、日期格式化器等可复用对象,正常关闭流程会自动清理这些缓存,监听器失效后缓存对象残留,就会被Tomcat的内存泄漏检测机制捕获,输出你看到的报错日志。
解决方案
- 替换适配Jakarta Servlet的Log4j依赖
把pom.xml中原有log4j-web依赖替换为适配Jakarta规范的版本,同时建议升级Log4j2到2.17.2及以上稳定版本,同时修复已知的安全漏洞:
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-jakarta-web</artifactId> <version>2.17.2</version> </dependency>
- 调整Log4j2配置适配web环境关闭逻辑
在你的log4j2.xml的<Configuration>标签上添加shutdownHook="disable"属性,禁用JVM层面的默认关闭钩子,交由web上下文监听器统一处理资源销毁,避免冲突:
<Configuration shutdownHook="disable"> <!-- 原有配置内容保持不变 --> </Configuration>
- (可选)无需极致性能可直接关闭ThreadLocal缓存
如果你的应用对日志写入性能要求不高,可以直接在Tomcat的启动参数中添加如下配置,彻底禁用Log4j2的ThreadLocal缓存优化,从根源避免这类内存泄漏问题:
-Dlog4j2.enableThreadlocals=false
内容的提问来源于stack exchange,提问作者laing
相关产品推荐
相关产品推荐

