文章目录
- 1 三色标记流程
- 1.1 初始标记
- 1.2 并发标记
- 1.3 重新标记
- 1.4 清除阶段(Sweep)
- 1.5 为什么初始标记和重新标记需要STW,而并发标记不需要?
- 2 并发标记的写屏障
- 3 多标问题
- 4.漏标问题
- 4.1 漏标的两个必要条件
- 4.2 解决方案一:增量更新(CMS)
- 4.3 解决方案二:原始快照(SATB,G1)
- 4.4 为什么 G1 选择 SATB?
- 5.面试回答模板
1 三色标记流程
三色标记算法是一种JVM中垃圾标记的算法,他可以减少JVM在GC过程中的STW时长,他是CMS、G1等垃圾收集器中主要使用的标记算法。
在出现三色标记算法之前,JVM中垃圾对象的标记主要采用可达性分析算法及引用计数法。但是这两种算法存在以下问题:
- 1、循环引用问题,如果两个对象互相引用,就形成了一个环形结构,如果采用引用计数法的话,那么这两个对象将永远无法被回收。
- 2、STW时间长,可达性分析的整个过程都需要STW,以避免对象的状态发生改变,这就导致GC停顿时长很长大大影响应用的整体性能。
为了解决上面这些问题,就引入了三色标记法.
三色标记法将对象分为三种状态:白色、灰色和黑色
- 白色:该对象没有被标记过
- 灰色: 该对象已经被标记过了,但该对象的引用对象还没标记完
- 黑色: 该对象已经被标记过了,并且他的全部引用对象也都标记完了,
三色标记法的标记过程可以分为三个阶段: 初始标记(Initial Marking)、并发标记 (Concurrent Marking)和重新标记 (Remark)。
1.1 初始标记
- 目标:标记所有GC Roots 直接引用的对象。
- 操作:
- 所有对象初始为 白色。
- 从 GC Roots(如虚拟机栈引用、方法区静态属性、本地方法栈引用等)出发,将 直接引用的对象 标记为 灰色。
- 初始标记阶段只扫描 GC Roots 的直接引用链,不深入遍历整个对象图。
- 是否 STW:是(Stop The World)。
- 特点:时间短,仅扫描根对象的直接引用。
1.2 并发标记
- 目标:从灰色对象出发,遍历整个对象图,标记所有可达对象。
- 操作:
- 从灰色集合中取出对象,将其标记为 黑色,并将其引用的白色对象标记为 灰色。
- 重复上述过程,直到灰色集合为空。
- 在此阶段,应用程序线程与 GC 线程并发执行,用户代码可能修改对象引用关系。
- 是否 STW:否(无需 Stop The World)。
- 关键问题:
- 并发修改的挑战:用户线程可能新增或断开引用,导致漏标或多标(如黑色对象新增引用白色对象)。
- 解决方案:使用 写屏障(Write Barrier) 技术,拦截对象引用的修改操作,并更新标记状态。
- 增量更新(Incremental Update):当黑色对象新增指向白色对象的引用时,记录该引用,后续重新扫描。
- 删除写屏障(Destructive Write Barrier):当白色对象的引用被断开时,将其标记为灰色。
- 耗时分析:
- 最耗时的阶段,需遍历整个对象图。
- 优势:通过并发执行,减少 STW 时间,提升系统响应性。
1.3 重新标记
- 目标:修正并发标记阶段中因用户线程修改对象引用导致的漏标或误标。
- 操作:
- 从灰色集合重新开始遍历对象图,修正标记状态。
- 处理 未被并发标记阶段遍历到的对象(如新增的引用)。
- 是否 STW:是(Stop The World)。
- 特点:
- 时间通常较短,因为只需修正少量错误
以上三个标记阶段中,初始标记和重新标记是需要STW的,而并发标记是不需要STW的。其中最耗时的其实就是并发标记的这个阶段,因为这个阶段需要遍历整个对象树,而三色标记把这个阶段做到了和应用线程并发执行,大大降低了GC的停顿时长
1.4 清除阶段(Sweep)
- 目标:回收所有 白色对象(不可达对象)。
- 操作:
- 遍历堆内存,回收白色对象的内存空间。
- 将黑色对象重置为白色,以便下次 GC 使用。
- 是否 STW:否(部分垃圾收集器可并发执行)。
1.5 为什么初始标记和重新标记需要STW,而并发标记不需要?
在初始标记阶段,针对根 (GCRoot)直接引用的对象进行标记,这个过程也通常被叫做根扫描。
为了防止在初始标记过程中根对象被修改,这个过程是STW的,虽然G1可以通过采用写屏障技术来获知对象是否发生了修改,但是因为大多数的GCRoot他并不是对象,所以无法被获知的,所以,这个阶段是需要进行STW的。
重新标记阶段,目的是修正并发标记阶段因应用程序继续运行而产生的任何变化(因为并发标记没有STW,所以会有变化)。此时,需要重新检查和更新那些在并发标记阶段可能发生变化的对象标记信息。
重新标记是清理前的最后一次标记,需要确保这个过程的准确性,所以需要做STW来保证。
总之,三个阶段,为了提升性能肯定是能不STW就不STW,而最后一个阶段一一重新标记因为是最终阶段,所以需要STW来确保准确性。而第一个阶段一初始标记,因为无法感知到GCRoot的变化,所以需要做STW来确保这个阶段的准确性。
2 并发标记的写屏障
并发标记过程中,应用程序线程可能会修改对象图,因此垃圾回收器需要使用写屏障 (Write Barrier) 技术来保证并发标记的正确性
写屏障是一种在对象引用被修改时,将其新的引用信息记录在特殊数据结构中的机制。在三色标记法中,写屏障技术被用于记录对象的标记状态,并且只对未被标记过的对象进行标记。
当应用程序线程修改了一个对象的引用时,写屏障会记录该对象的新标记状态。如果该对象未被标记过,那么它会被标记为灰色,以便在垃圾回收器的下一次遍历中进行标记。如果该对象已经被标记为可达对象,那么写屏障不会对该对象进行任何操作。
通过使用写屏障技术,可以使得三色标记法过程中标记更加准确。然而,尽管写屏障对于维护垃圾收集器的准确性至关重要,它们仍然存在一些局限性,
- 1.性能开销.: 写屏障会引入额外的性能开销,因为每次对象引用更新时都需要执行额外的代码。这种开销可能导致系统性能下降,尤其是在高度并发的场景中
- 2.并发修改的挑战: 在高度并发的应用中,对象的引用可能会频繁变化。写屏障需要在每次引用变化时及时更新信息,但在极端并发条件下,可能难以捕捉到所有的变化。
- 3.保守策略导致的多标: 为了避免误删除有效对象,一些垃圾收集器可能采取保守策略,在存在不确定性时选择保留对象。这可能导致实际上已经不再使用的对象被错误地标记为存活。
- 4.优化策略的双刃剑: 为了减轻性能开销,某些垃圾收集器可能采用优化策略,例如只在特定条件下激活写屏障。这种优化有可能导致某些引用更新被错过,影响标记的准确性。
3 多标问题
所谓多标,其实就是这个对象原本应该被回收掉的白色对象,但是被错误的标记成了黑色的存活对象。从而导致这个对象没有被GC回收掉。
这个一般发生在并发标记过程中,该对象还是有引用的,但是在过程中,应用程序执行过程中把他的引用关系删除了,导致他变成了一个垃圾对象,
多标的话,会产生浮动垃圾,这个问题一般都不太需要解决,因为这种垃圾一般都不会太多,另外在下一次GC的时候也都能被回收掉。
4.漏标问题
4.1 漏标的两个必要条件
- 黑色对象新增引用白色对象(条件①)
- 黑色对象(已标记为存活)新增指向白色对象(未被标记)的引用,导致白色对象被漏标。
- 灰色对象删除对白色对象的引用(条件②)
- 灰色对象(正在扫描)在扫描完成前断开对白色对象的引用,导致白色对象失去所有路径连接。
漏标必须同时满足这两个条件才会发生。因此,解决方案只需要 破坏其中一个条件 即可。
4.2 解决方案一:增量更新(CMS)
目标:破坏条件①(黑色对象新增引用白色对象)
- 核心思想:如果黑色对象新增了对白色对象的引用,就 将黑色对象重新标记为灰色,并在后续重新扫描其引用链。这样白色对象会被标记为灰色,避免漏标。
- 具体操作:
- 写屏障:当黑色对象新增引用白色对象时,记录该引用关系。
- 重新标记阶段:以这些新增的引用为起点,重新扫描黑色对象的引用链。
- 类比:
- 假设你在打扫房间(GC),已经标记某个抽屉(黑色对象)里的物品为“必须保留”。此时家人往抽屉里放了新东西(白色对象)。
- 增量更新就像家人给你留个便条:“这个抽屉有新增物品,请重新检查”,你就会回去重新扫描抽屉,确保新物品不被遗漏。
- 优点:
- 不产生浮动垃圾(所有被引用的对象都会被正确标记)。
- 缺点:
- 需要重新扫描整个引用链,耗时较长(尤其是引用链复杂时)
4.3 解决方案二:原始快照(SATB,G1)
目标:破坏条件②(灰色对象删除对白色对象的引用)
- 核心思想:在 GC 开始时,记录所有存活对象的状态(快照)。即使后续引用被删除,也以快照为准,确保白色对象不会被漏标。
- 具体操作:
- 写屏障:当灰色对象删除对白色对象的引用时,记录该白色对象为“快照中的存活对象”。
- 重新标记阶段:以这些白色对象为起点,重新扫描它们的引用链。
4.4 为什么 G1 选择 SATB?
- 性能优先:
- SATB 只需扫描被删除引用的对象(通过 RSet 和 Card Table 快速定位),而增量更新需要重新扫描整个引用链。
- 对于大堆场景(如 G1 的 Region 划分),SATB 的效率优势更明显。
- 浮动垃圾容忍度高:
- 浮动垃圾只是延迟到下一轮 GC 清理,不会影响当前 GC 的准确性。相比而言,增量更新的耗时可能影响系统响应时间。
- 与 Region 结构适配:
- G1 的 Region 结构天然支持 RSet(记录跨 Region 引用),方便快速定位被删除引用的对象。
5.面试回答模板
垃圾回收机制需要分为两个步骤进行,第一标记出哪些是需要回收的垃圾,第二根据相应的清除机制如:标记复制、标记清除、标记整理等,而三色标记就是为了避免标记垃圾时STW的问题,三色标记将对象分为三种颜色,白色、灰色、黑色、其中白色表示未被引用的对象也是需要删除的对象,灰色表示对象已经被标记,但其引用还未标记,黑色表示对象及其引用都已经被标记,整个标记过程主要分为三个阶段:初始标记、并发标记和重新标记,初始标记主要是从GC Root出发,直接将引用对象标记为灰色,此时会引起stw,但耗时较短,并发标记从灰色对象出发,遍历整个对象图,耗时最长,但是不会引起stw,会和工作线程并发执行,但是并发执行期间会产生一些浮动的垃圾,这些浮动垃圾需要依靠重新标记去修正,此时也会引起stw,防止产生更多的浮动垃圾,并发标记阶段会通过写屏障来保证并发标记的准确性,针对漏标的问题一般可以通过增量更新或快照的方式去解决