标记-清除法通过从gc roots出发的确定性图遍历精准标记活跃对象,确保不遗漏、不重复、不断链;gc roots包括虚拟机栈局部变量、静态字段、jni全局引用等天然可达入口,标记阶段采用防重入递归遍历,严格依据可达性分析判定对象存活与否。

标记清除法的“精准标记活跃对象”,关键不在根节点数量多少,而在于遍历路径不遗漏、标记状态不重复、引用关系不中断。它不是靠“猜”或“统计”,而是用确定性的图遍历机制完成可达性判定。
一、明确哪些东西算 GC Roots
GC Roots 不是任意对象,而是天然可达、无需被其他对象引用就能被程序直接访问的一组入口点。常见包括:
- 虚拟机栈中每个线程的局部变量表里正在使用的引用(如方法参数、临时变量)
- 方法区中的类静态属性(static 字段)所引用的对象
- 本地方法栈(JNI)中注册的全局引用(Global Reference)
- 被同步锁(synchronized)持有的对象(即 monitor 持有者)
- JVM 内部关键对象,如基本类型的 Class 对象、常量池中字符串等
二、标记阶段的核心操作:递归+防重入遍历
标记不是一次性扫完所有对象,而是从每个 Root 出发,沿引用链逐层推进。核心逻辑是:
- 对每个 Root 引用的对象,若其标记位为 false,则设为 true,并将其所有直接子对象(即该对象字段中引用的其他对象)加入待处理队列
- 持续从队列取对象,重复上述过程,直到队列为空
- 每个对象在标记前都检查标记位——已标则跳过,避免循环引用导致无限递归或重复标记
这本质上是深度优先(DFS)或广度优先(BFS)的图遍历,把堆看作一张由引用构成的有向图,Root 是起点,所有能走到的节点就是活跃对象。
三、为什么能“精准”?靠的是可达性定义,不是经验判断
所谓“精准”,是指它严格遵循可达性分析(Reachability Analysis)原则:
- 一个对象只有从任意 GC Root 出发,经过任意长度的引用链都能到达,才被认定为活跃
- 即使对象之间互相引用(比如 A→B 且 B→A),只要它们和 Root 之间没有路径,仍会被判为垃圾
- 标记过程不依赖引用计数,不受循环引用干扰,这是它比引用计数法更可靠的根本原因
因此,“精准”不是指毫秒级响应或零误差,而是指逻辑完备、无漏判、无误判(在 stop-the-world 前提下)——只要遍历完整、标记位管理正确,结果就唯一确定。











