java垃圾回收通过根搜索算法判定对象可达性:从gc roots(如栈帧变量、静态属性、常量池对象等)出发遍历引用链,可达对象存活,不可达对象可回收;该过程与引用类型、finalize无关。

Java 垃圾回收通过根搜索算法(Root Scanning / Reachability Analysis)判断对象是否可达,核心逻辑是:从一组被称为“GC Roots”的对象出发,沿着引用链向下遍历,所有能被访问到的对象视为可达(live),其余对象则被判定为不可达(unreachable),可被回收。
哪些对象可以作为 GC Roots?
GC Roots 不是固定类或实例,而是 JVM 在特定时刻认定的“绝对不能被回收”的起点。主要包括:
- 虚拟机栈(栈帧中的局部变量表)中引用的对象:比如方法中 new 出来的对象、传入的参数、临时变量等;
- 本地方法栈中 JNI(Native 方法)引用的对象:Java 调用 C/C++ 时传递或持有的 Java 对象;
-
方法区中类静态属性引用的对象:如
public static List<string> cache = new ArrayList();</string>中的 ArrayList 实例; -
方法区中常量引用的对象:例如字符串常量池里被直接引用的字符串对象(如
String s = "hello";中的 "hello"); - Java 虚拟机内部的特殊对象:如基本类型 Class 对象、一些关键异常对象(OutOfMemoryError)、系统类加载器等。
根搜索过程是怎么进行的?
这是一个广度优先(BFS)或深度优先(DFS)的图遍历过程,JVM 会:
- 暂停用户线程(Stop-The-World,STW),确保堆状态一致;
- 枚举所有 GC Roots,并将它们加入初始扫描队列;
- 逐个取出对象,检查其成员变量(即引用字段),把非 null 的引用对象加入队列;
- 重复该过程,直到队列为空;
- 遍历结束后,未被标记的对象即为不可达对象,进入待回收集合。
注意:这个过程不依赖对象是否重写了 finalize() 或是否有 Reference 子类(如 WeakReference),那些属于后续的回收策略阶段,不是可达性判定本身。
常见误解与关键细节
很多人混淆“引用存在”和“可达”,以下几点需特别注意:
- 循环引用不影响判定:A 引用 B,B 引用 A,但二者都不被任何 GC Root 直接或间接引用 → 两者均不可达,会被同时回收;
- 局部变量作用域结束 ≠ 立即不可达:只要栈帧还在,局部变量仍算 GC Root;即使代码已执行到方法末尾,只要栈帧未出栈(如正在处理 finally 或尚未返回),对象仍可能存活;
- 软/弱/虚引用不改变可达性等级:只有强引用参与根搜索;软引用(SoftReference)等只在内存不足或 GC 阶段后期才被考虑清除,不影响初始可达性判定;
-
Finalizer 机制已被弃用:Java 9 起
Object.finalize()被标记为 deprecated,且 finalize 队列处理是独立于根搜索的异步流程,不参与可达性分析。
如何验证对象是否真的不可达?
开发中可通过工具辅助观察:
- 使用
jmap -histo <pid></pid>查看堆中对象数量变化; - 配合
jstat -gc <pid></pid>观察 YGC / FGC 后对象是否减少; - 在关键位置插入
System.gc()(仅用于测试,不推荐生产使用)并配合 VisualVM 或 JConsole 查看堆快照; - 借助 MAT(Eclipse Memory Analyzer)打开 heap dump,用“Dominator Tree”或“Path to GC Roots”功能反向追踪引用链,确认为何某个对象没被回收。
真正决定一个对象能否被回收的,永远是它能否从 GC Roots 出发被引用链抵达 —— 和对象大小、创建时间、是否实现某接口都无关。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











