Java类与对象循环引用本质是强引用闭环,导致内存释放困难、序列化失败或深拷贝栈溢出;解决需分场景:设计时规避强闭环、序列化时配置ID引用或跳过循环、深拷贝/遍历时用IdentityHashMap防递归、toString中用ThreadLocal缓存防栈溢出。

Java 中类与对象的循环引用问题,本质是强引用闭环导致内存无法释放、序列化失败或深拷贝栈溢出。它不等于内存泄漏(JVM 的可达性分析能处理无外部引用的纯循环对象),但会在实际开发中引发具体故障——比如 Gson 报 StackOverflowError、Jackson 序列化卡死、clone() 崩溃,或双向链表长期驻留堆中阻碍回收。解决思路不是“一刀切禁止”,而是按场景选合适手段。
设计阶段主动规避强闭环
这是最轻量也最推荐的做法,从源头降低风险:
- 父子关系类(如
Parent↔Child、Node↔ParentNode)中,只让一方持强引用,另一方用WeakReference或SoftReference存储反向指针 - 避免在 DTO、VO、Entity 层直接暴露双向导航属性;如必须保留,用
@JsonIgnore(Jackson)、@XmlTransient(JAXB)或@JsonBackReference/@JsonManagedReference显式标注方向 - 集合类内部维护关系时,优先用单向结构(如树只存子节点列表),需要向上查时通过工具方法临时遍历,而非固化
parent字段
序列化时跳过或标记循环
当对象图天然含环(如组织架构、图结构),序列化器默认会无限递归。可配置处理策略:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- Jackson:启用 `@JsonIdentityInfo` 注解,为每个对象生成唯一 ID,后续引用改写为 ID 引用
-
Gson:引入
GraphAdapterBuilder(Gson extras 模块),将对象图转为 ID-映射表形式输出 -
XML / JSON 库通用方案:设置全局循环引用策略,如 Jackson 的
SerializationFeature.FAIL_ON_SELF_REFERENCES设为false,配合SimpleModule添加自定义序列化器
深拷贝与运行时遍历时防栈溢出
手动实现 clone() 或遍历对象图(如权限检查、日志打印)时,需主动记录已访问对象:
立即学习“Java免费学习笔记(深入)”;
- 用
IdentityHashMap<Object, Void>缓存已处理实例(注意必须用 IdentityHashMap,避免 equals 误判) - 每次进入新对象前先查缓存,命中则返回占位符(如
"[Circular Reference]")或跳过递归 - 第三方库更省心:Apache Commons Lang 的
SerializationUtils.clone()基于 Java 原生序列化,天然支持循环;Kryo 开启setReferences(true)即可自动处理
toString 等调试方法安全兜底
重写 toString() 时若直接拼接关联对象,极易因循环调用触发栈溢出。简单可靠的防护方式:
- 使用
ThreadLocal<IdentityHashMap<Object, Void>>记录当前线程已进入的对象 - 首次访问时放入 map 并继续拼接;再次遇到同一实例时,仅输出类名 + identityHashCode,不递归
- 避免在
toString()中调用业务逻辑方法或触发懒加载,防止意外引入新引用链

















