前言
最近在回看 JVM 的相关概念,看到了三色标记法的使用,所以就记录一下。
三色标记法是什么
在可达性分析的过程中,对象有且只有三种状态:完全没被扫描,对象被扫描但子对象还没完全被扫描,对象与其子对象都被扫描了。
而这三种状态即为三种颜色
1 | 白色:完全没被扫描。对象与子对象确定被回收。 |
出现的原因
三色标记法是对堆上的对象进行归类,简化可达性分析中的重复扫描的工作量。
作用阶段
作用阶段包括初始标记与并发标记。
初始标记主要是确定 GC Root ,并把根节点标为灰色。初始标记需要暂停用户线程。
并发标记主要是根据 GC Root 往下扫描子对象,将子对象从白色扭转为黑色/灰色。并发标记过程标记线程与用户线程并行。
可能出现的问题
在进行回收前,对象有且只会存在两个颜色:黑色以及白色。
但由于扫描所有对象耗时久,所以扫描只能与用户线程并发,并发过程中会发生对象图关系的改变,会出现两个问题
- 黑色对象变成了白色对象,最终应该被回收但未被回收,俗称浮动垃圾,下一次回收也行。
- 白色对象变成了黑色对象,最终不应被回收却被回收了,会导致程序异常。
问题的原因
白色对象变成黑色对象的原因,需要同时满足下面两个:
- 黑色根对象增加了对上述白色对象的引用。
- 灰色根对象删除对白色子对象引用。
最后的结果是:白色对象实际上是黑色对象,但回收程序仍认为是白色对象。
问题的解决
针对上述的变动,解决办法有两个:
- 增量更新:记录黑色根对象对白色对象的新增调用,并且以黑色根对象为根重新扫描这部分的引用。
- 原始快照:记录灰色根对象删除对白色子对象的删除,并且重新扫描整个对象图。
增量更新 | 原始快照 | |
---|---|---|
优点 | 扫描范围小,最终标记过程停顿时间短 | 不会产生浮动垃圾,根据对象图重新扫描 |
缺点 | 产生新的浮动垃圾。仅记录新增引用,未记录删除引用 | 最终标记时间长。扫描了整个对象图 |
适用范围 | 老年代。老年代对象引用变化少,上述缺点出现概率低 | 基于 Region 实现的垃圾收集器 |
最后的叨叨
为什么会有并发标记与最终标记两阶段
我觉得主要是为了尽可能缩短 STW 的时间。
两阶段更像是二八原则的具象化。80%对象关系不会变化,20%对象关系会变化(实际上更少)。
针对不会变化的部分,并发标记能减少业务感知的时间。针对变化的部分,则记录下来再 STW 确定最终引用关系。
本文首发于cartoon的博客
转载请注明出处:https://cartoonyu.github.io