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

Tomcat会话复制序列化:如何展示对象引用图及日志记录

解决Tomcat会话复制序列化引用链追踪问题

针对你遇到的Tomcat会话复制时的NotSerializableException,要展示完整的对象引用链(比如会话中的对象A -> 对象B -> ... -> ClassFoo),以及追踪每个writeObject()处理的对象类型,有以下几种可行方案:

一、获取序列化对象引用链

1. 自定义ObjectOutputStream扩展

继承JDK的ObjectOutputStream,重写writeObject0方法,在序列化每个对象时记录其类型和调用栈,当抛出NotSerializableException时,就能回溯出完整的引用路径。示例代码:

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggingObjectOutputStream extends ObjectOutputStream {
    private static final Logger log = LoggerFactory.getLogger(LoggingObjectOutputStream.class);

    public LoggingObjectOutputStream(OutputStream out) throws IOException {
        super(out);
    }

    @Override
    protected void writeObject0(Object obj, boolean unshared) throws IOException {
        log.debug("正在序列化对象: {}", obj.getClass().getName());
        try {
            super.writeObject0(obj, unshared);
        } catch (NotSerializableException e) {
            log.error("序列化失败,当前对象: {}", obj.getClass().getName(), e);
            throw e;
        }
    }
}

在Tomcat会话复制的逻辑中替换默认的ObjectOutputStream为这个自定义实现,就能拿到每个序列化对象的日志,进而梳理出引用链。

2. 开启JVM序列化调试日志

添加JVM启动参数:

-Dsun.io.serialization.extendedDebugInfo=true

开启后,NotSerializableException的栈追踪会附带额外信息,比如当前正在序列化的对象字段名称,结合栈帧里的writeOrdinaryObject、defaultWriteFields等方法,就能倒推哪个对象的哪个字段引用了未序列化的ClassFoo。

3. IDE调试追踪

在IDEA或Eclipse中,在java.io.ObjectOutputStream.writeObject0方法处设置断点,断点条件设为obj instanceof com.example.ClassFoo或者当抛出NotSerializableException时暂停。此时查看栈帧的局部变量,比如每个栈帧里的obj参数,就能一步步往上找到引用ClassFoo的上层对象,直到会话中的根对象。

二、Java序列化的日志记录方案

  • 自定义日志工具:用上面的LoggingObjectOutputStream,结合日志框架(如SLF4J),可以记录所有被序列化的对象类型、字段信息,甚至引用关系。
  • 字节码增强动态日志:用ASM、ByteBuddy等字节码工具,动态修改ObjectOutputStream的方法,插入日志逻辑,无需修改业务代码就能实现全局序列化日志。
  • Tomcat专属日志:将Tomcat的org.apache.catalina.ha.session.DeltaManager日志级别设为DEBUG,会输出会话中哪些属性被序列化、哪些发生变更,能缩小排查范围,辅助定位问题对象。

你提供的报错栈追踪:

java.io.NotSerializableException: com.example.ClassFoo
        at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1175)
        at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1543)
        at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1500)
        at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1423)
        at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1169)
        at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1543)
        at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1500)
        at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1423)
        at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1169)
        at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1543)
        at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1500)
        at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1423)
        at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1169)
        at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:345)
        at org.apache.catalina.ha.session.DeltaRequest$AttributeInfo.writeExternal(DeltaRequest.java:438)
        at org.apache.catalina.ha.session.DeltaRequest.writeExternal(DeltaRequest.java:326)
        at org.apache.catalina.ha.session.DeltaRequest.serialize(DeltaRequest.java:340)
        at org.apache.catalina.ha.session.DeltaSession.getDiff(DeltaSession.java:168)
        at org.apache.catalina.ha.session.DeltaManager.requestCompleted(DeltaManager.java:1046)
        at org.apache.catalina.ha.session.DeltaManager.requestCompleted(DeltaManager.java:1014)
        at org.apache.catalina.ha.tcp.ReplicationValve.send(ReplicationValve.java:527)
        at org.apache.catalina.ha.tcp.ReplicationValve.sendMessage(ReplicationValve.java:515)
        at org.apache.catalina.ha.tcp.ReplicationValve.sendSessionReplicationMessage(ReplicationValve.java:497)
        at org.apache.catalina.ha.tcp.ReplicationValve.sendReplicationMessage(ReplicationValve.java:409)
        at org.apache.catalina.ha.tcp.ReplicationValve.invoke(ReplicationValve.java:340)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 07:14:56