GC Roots是JVM强持有的不可回收对象,包括虚拟机栈局部变量、方法区静态属性与常量、本地方法栈JNI引用及被synchronized持有的对象;GC从这些根出发沿引用链遍历标记存活对象。

Java的GC扫描不是逐个检查所有对象,而是从一组“根对象”(GC Roots)出发,沿着引用链向下遍历,把能到达的对象标记为“存活”,其余则视为可回收。这个过程依赖JVM对引用关系的精确建模和运行时维护。
哪些对象算作GC Roots?
GC Roots是引用链的起点,它们本身不被任何Java对象引用,但被JVM强持有,因此永远不可回收。常见类型包括:
- 虚拟机栈(栈帧中的局部变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象(如字符串常量池里的String实例)
- 本地方法栈中JNI引用的对象
- 正在被同步锁(synchronized)持有的对象(部分GC算法会纳入)
引用链如何构建与遍历?
JVM在对象头或元数据中记录类型信息,GC线程能根据对象类型准确识别其所有引用字段(包括父类字段)。遍历时:
- 从每个Root对象开始,读取其直接引用的字段值(即对象地址)
- 将非空引用指向的对象加入待扫描队列(如使用三色标记中的“灰色”集合)
- 重复取出并扫描新对象的引用字段,直到队列为空
- 未被访问到的对象即不在引用链上,判定为“不可达”
注意:数组对象会被整体视为一个节点,其元素引用在扫描该数组实例时一并处理;基本类型字段(int、boolean等)不参与引用链构建。
立即学习“Java免费学习笔记(深入)”;
不同引用类型对遍历的影响
Java的四种引用(强、软、弱、虚)并不改变引用链的“存在性”,而是影响GC在标记-清除阶段的**后续决策**:
- 强引用:只要在引用链上,就一定不被回收
- 软引用:在内存不足且对象仅被软引用可达时,才可能被回收(遍历仍会走到它)
- 弱引用:GC遍历时照常访问,但标记阶段结束后,仅被弱引用可达的对象会被立即清除
- 虚引用:不影响对象生命周期,无法通过虚引用获取对象实例,仅用于接收回收通知
也就是说,软/弱/虚引用本身是堆上的Reference子类实例,它们的referent字段构成引用链的一部分;但GC算法会对这些Reference对象做特殊处理(如将它们加入pending队列),而不是简单忽略。
对象内部引用的可见性保障
JVM确保引用字段的更新对GC线程可见,主要依靠:
- 写屏障(Write Barrier):在给引用字段赋值时插入额外逻辑,例如记录跨代引用(用于G1/CMS的卡表维护)
- 安全点(Safepoint)机制:保证GC线程暂停应用线程时,对象状态处于一致、可分析的快照
- 精确式GC:现代JVM不再依赖保守扫描,而是通过Class Metadata精准知道每个对象有哪些引用字段及偏移量
这使得引用链遍历既高效又可靠,不会漏掉活跃对象,也不会误判已失效引用。
基本上就这些。引用链不是静态结构,而是在每次GC发生时动态构建的快照;它的起点、路径和终点,由运行时对象图和引用类型共同决定。










