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

Tomcat9升级后异步进程触发请求对象已回收IllegalStateException问题

问题背景

代码库核心事件突然报错,错误信息统一,仅run()方法的参数类存在差异:

2024-11-06 22:32:12,435 AuditService ERROR accounts.service.audit.AuditService run(ProfileChangeRequestDenied): java.lang.IllegalStateException: The request object has been recycled and is no longer associated with this facade

这段代码多年未改动,升级到Tomcat 9后才出现该错误。调研发现Tomcat 9将org.apache.catalina.connector.RECYCLE_FACADES的默认值从false改为了true。目前已知问题是代码尝试访问已被回收的请求对象,但该基类被广泛使用,希望先通过临时补丁争取时间进行彻底修复。

引发错误的ServiceEvent类精简版本如下,调试定位到错误出现在getUserAgent方法的servletRequest.getHeader调用处:

import javax.servlet.http.HttpServletRequest;

public abstract class ServiceEvent {
    private final HttpServletRequest servletRequest;

    public ServiceEvent(HttpServletRequest servletRequest) {
        this.servletRequest = servletRequest;
    }

    public String getUserAgent() {
        String userAgent = null;
        if (null != servletRequest) {
            userAgent = servletRequest.getHeader(USER_AGENT);
        }
        return (userAgent == null) ? UNKNOWN : userAgent;
    }

    public abstract String getDetails();
}

尝试在Tomcat环境变量配置脚本中添加以下配置,但未生效:

CATALINA_OPTS="-D-Dorg.apache.catalina.connector.RECYCLE_FACADES=true ${CATALINA_OPTS}"

疑问

  1. 我的假设是否正确?修改RECYCLE_FACADES的值能否恢复原有行为?
  2. 我的配置方式是否正确?
  3. 是否还有其他遗漏的要点?

注:从事软件工程多年,但使用Java仅约一年。


问题解答

1. 假设正确性

你的假设完全正确。Tomcat 9把RECYCLE_FACADES默认值改为true后,请求处理完成会立即回收请求facade对象,此时再调用该对象的方法就会抛出IllegalStateException。将该参数设为false就能恢复Tomcat 8及更早版本的行为:请求facade不会被主动回收,哪怕请求处理完成,仍然能访问其方法(虽然这会带来内存泄漏风险,但确实能临时解决当前报错问题)。

2. 配置方式错误

你的配置里多了一个-D,JVM参数的正确格式是单个-D前缀,而且要设为false才会改变默认行为,正确配置应为:

CATALINA_OPTS="-Dorg.apache.catalina.connector.RECYCLE_FACADES=false ${CATALINA_OPTS}"

另外要确保配置脚本生效:

  • Linux/Unix系统:确认修改的是catalina.sh或对应环境变量脚本,重启Tomcat后用ps aux | grep tomcat查看进程参数,确认该配置已加载。
  • Windows系统:修改catalina.bat后同样需要重启Tomcat验证。

3. 其他遗漏要点

  • 临时补丁的风险:把RECYCLE_FACADES设为false会导致请求facade对象无法及时回收,长期运行可能引发内存泄漏甚至OutOfMemoryError,这只是权宜之计,必须尽快推进彻底修复。
  • 彻底修复方向:不要在请求处理完成后持有HttpServletRequest的引用,应该在请求生命周期内(比如Controller/Service方法执行时)提前提取需要的信息(比如User-Agent),存储到ServiceEvent类中,而非直接持有请求对象。例如修改ServiceEvent:
import javax.servlet.http.HttpServletRequest;

public abstract class ServiceEvent {
    private final String userAgent;

    public ServiceEvent(HttpServletRequest servletRequest) {
        this.userAgent = servletRequest != null ? servletRequest.getHeader(USER_AGENT) : UNKNOWN;
    }

    public String getUserAgent() {
        return userAgent;
    }

    public abstract String getDetails();
}
  • 验证参数是否生效:可以在代码中添加日志输出System.getProperty("org.apache.catalina.connector.RECYCLE_FACADES"),确认参数是否被正确加载。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 12:27:44