1. 概述

本文将深入探讨 JVM 是如何处理那些不可达但存在循环引用的对象回收问题。

我们会先简要回顾几种常见的垃圾回收(GC)算法,然后重点分析 JVM 是如何解决循环引用带来的内存泄漏风险的。

需要特别说明的是,垃圾回收机制本身并不属于 JVM 规范的强制要求,而是由 JVM 实现方自行决定(参考 JVM 规范文档)。这意味着不同的 JVM 实现可能采用完全不同的 GC 策略,甚至不实现 GC。

本文聚焦于 HotSpot JVM 的实现细节。后文中提到的 JVM,如无特别说明,均指 HotSpot JVM。

2. 引用计数法

引用计数是一种直观的垃圾回收策略:每个对象维护一个“引用计数”,记录当前有多少变量指向它。✅ 只要引用数大于 0,对象就被视为存活

这个计数通常存储在对象头(Object Header)中。在最朴素的实现中:

  • 每当有新引用指向该对象,计数器原子性地 +1
  • 每当引用被清除或重置,计数器原子性地 -1
  • 当计数归零时,对象立即被回收

比如 Swift 语言就采用了引用计数(ARC)来管理内存。❌ 但 JVM 并没有使用引用计数作为其 GC 算法的基础

2.1 优缺点分析

优点

  • 内存释放即时:对象一旦无引用,立刻回收,避免长时间停顿(GC pause)
  • 回收成本分散:不会出现集中式的“Stop-The-World”暂停

⚠️ 缺点

  • 原子操作开销大:频繁的增减操作在多线程环境下性能损耗明显
  • 存在优化手段:如延迟计数(deferred)、缓冲计数(buffered)等可缓解问题

致命缺陷:无法处理循环引用

举个典型例子:对象 A 引用 B,B 又引用 A。当整个结构脱离主对象图后,虽然逻辑上已不可达,但由于彼此引用,计数始终 ≥1,导致内存泄漏。

这在实际开发中并不少见,比如双向链表就是典型场景。

假设一开始有一个外部对象 D 持有链表头引用:

Reachable List

此时链表可达,不应被回收,引用计数正确。

但当 D 被置为 null 或超出作用域后,链表整体已不可达:

Unreachable List

问题来了:尽管链表已“死亡”,但节点间的相互引用使得引用计数无法归零。在纯引用计数机制下,这些对象将永远无法被回收 —— 踩坑预警!

3. 追踪式垃圾回收(Tracing GC)

与引用计数不同,追踪式 GC 不关心“有多少人引用你”,而是问:“你能从根上找到吗?”

核心思想:从一组已知存活的“GC Roots”出发,沿引用链遍历整个对象图。所有能被访问到的对象标记为存活,其余不可达对象即为垃圾。

其工作流程类似于经典的三色标记算法

  1. 初始:所有对象为白色(未访问)
  2. 从 GC Roots 开始遍历,灰色表示正在处理
  3. 遍历完成后,仍为白色的对象即为垃圾

示意图如下:

Tracing Collector

3.1 什么是 GC Roots?

GC Roots 是 JVM 明确认定为“绝对存活”的对象,主要包括:

  • ✅ 当前栈帧中的局部变量(包括方法参数)
  • ✅ 正在运行的线程(Thread 对象)
  • ✅ 所有静态变量(static fields)
  • ✅ 由系统类加载器加载的类(Class 对象)
  • ✅ JNI 全局/局部引用

3.2 优势:天然免疫循环引用

由于追踪式 GC 依赖可达性分析,而不是引用数量,因此 循环引用不会成为障碍

哪怕对象之间形成复杂环路,只要整体无法从 GC Roots 到达,就会被正常回收。上图中即使 B ↔ C 相互引用,只要 A 不可达,整个子图都会被清理。

这也是为什么现代 JVM GC 都采用追踪机制的重要原因。

3.3 HotSpot JVM 的实现

截至当前版本,HotSpot JVM 的所有 GC 实现均为追踪式收集器,包括:

  • ✅ CMS(Concurrent Mark-Sweep)
  • ✅ G1(Garbage-First)
  • ✅ ZGC(Z Garbage Collector)
  • ✅ Shenandoah

结论明确:Java 开发者无需担心循环引用导致的内存泄漏问题 —— JVM 已经帮你搞定。

💡 补充知识:有些语言(如 Python)采用“引用计数 + 追踪 GC”混合模式,用追踪 GC 定期清理循环引用。而 JVM 从一开始就选择了更彻底的追踪路线。

4. 总结

  • ❌ 引用计数无法解决循环引用,不适合 JVM 的设计目标
  • ✅ HotSpot JVM 使用追踪式垃圾回收器,通过 GC Roots 可达性分析判断对象生死
  • ✅ 循环引用在 JVM 中不会导致内存泄漏,GC 会正常回收不可达的对象图
  • 📚 如需深入理解 GC 原理,推荐阅读《The Garbage Collection Handbook》

简单粗暴地说:放心写双向引用,JVM 懂你


原始标题:Garbage Collection and Cyclic References in Java

» 下一篇: Maven 日志配置详解