循环引用导致json序列化死循环,表现为stackoverflowerror、空响应或响应截断;需通过日志、响应特征及手动序列化复现来定位,并检查dto双向关联。

遇到接口返回 JSON 时卡住、超时或直接抛 StackOverflowError,大概率是循环引用导致的序列化死循环。这类问题不报明确异常,调试时容易误判为业务逻辑或网络问题,需从结果表现反向定位。
看日志和响应体特征
先确认是不是循环引用引发的问题:
- 服务端日志出现
java.lang.StackOverflowError,堆栈反复在com.fasterxml.jackson...或com.alibaba.fastjson...包内跳转; - 前端收到空响应、500 错误,或响应被截断(比如只显示一半 JSON 就中断);
- 用
curl或 Postman 直接调用接口,响应迟迟不返回,或返回极长的嵌套 JSON(如{"user":{"order":{"user":{"order":{...}}}}); - 本地用 Jackson 的
ObjectMapper.writeValueAsString(obj)手动序列化返回对象,复现相同异常。
查对象图结构
找到接口返回的 DTO 或实体类,重点检查是否存在双向关联:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 父子关系:如
User持有List<order></order>,Order又持有User; - 树形结构:如
Node类同时有parent和children字段; - Lombok 的
@Data或@ToString自动生成了包含循环字段的toString(),日志打印时也会触发递归(虽不直接影响 JSON,但会干扰排查)。
用工具快速验证
不改代码也能快速验证是否是序列化环节出问题:
- 在测试平台或网关层加一层日志,把接口返回值先用
Hessian2或Kryo序列化(它们对循环引用更宽容),若能成功,基本锁定是 JSON 序列化器的问题; - 临时在 Controller 返回前加一行:
System.out.println(new ObjectMapper().writeValueAsString(result));,看是否当场抛错; - 用 Arthas 的
watch命令监控序列化入口方法,如com.fasterxml.jackson.databind.ObjectMapper#writeValueAsString,观察入参对象是否含深层嵌套。
区分真实场景与伪循环
有些“看起来像循环”的结构其实不会触发问题:
- 单向引用(只有
A → B,没有B → A)不会循环; - 字段被
@JsonIgnore、@Transient或static修饰,序列化器会跳过; - 使用
@JsonIdentityInfo后,Jackson 会自动用 ID 引用代替重复对象,不再递归展开。
定位清楚后,再选对应方案解决,比如加注解、换序列化器,或重构 DTO 层剥离循环依赖。不复杂但容易忽略。










