三色标记法 :在三色标记法中存在三种颜色,白色,灰色,黑色 白色:表示对象尚未被垃圾收集器访问过。 灰色:表示该对象已经被垃圾收集器访问过,但是这个对象上至少还存在一个引用还没有被扫描过。
为什么引入三色标记法 为了提供 JVM 垃圾回收的性能,从 CMS 垃圾收集器开始,引入了并发标记的概念(此处的并发标记是指与用户线程一起工作)。 很明显在并发的情况下,“两色“的标记法是无法满足要求的。 三色标记的过程 为了解决并发的问题,我们可以引入中间的状态(灰色),当一个对象被标记的时候,会有下面几个过程: 首先被标记成灰色 检测当前所有的灰色对象,遍历子节点 如果子节点被遍历完了,把当前节点标记成黑色 在上图中,第一个过程是把 A,E 标记成灰色,后续再遍历 A,E 的子节点,发现了 A 有 C 节点,E 有 F 节点,这样 C,F在后续就会被标记成活着的对象(此处还会存在缺陷,后面讨论) 三色标记的问题 从上面的分析可以得出,三色标记法解决了并发的场景的引用链变动的问题,但是也会存在问题。
三色标记(Tri-Color-Marking) 垃圾收集器在并发标记的过程中,执行标记期间应用线程还在并行运行,对象间的引用关系时刻发生变化,垃圾收集器在标记过程中就容易发生多标和漏标(其实多标和漏标我们统称为误标 针对这一问题我们通过 “三色标记 (Tri-Color-Marking)” 作为理论工具来辅助推导,将垃圾收集器遍历对象引用的过程中,“按照是否访问过” 这个条件标记成三种颜色。 三色标记示例代码(示例来源于网络): public class TriColorMarking { public static void main(String[] args) { 这是一个典型的 “多标” 场景。 下面我们会通过并发标记的过程中出现的漏标和多标场景进行分析。 漏标 在并发标记过程中,将原本消亡的对象标记为存活对象,这就是漏标。 解决漏标和多标 解决漏标和多标分别有两种解决方案:增量更新(Incremental Update) 和原始快照(Snapshot At The Beginning, STAB) 增量更新(Incremental
三色标记法 GC 垃圾回收器其主要的目的是为了实现内存的回收,在这个过程中主要的两个步骤就是:内存标记,内存回收。 三色标记法简介 三色标记法,主要是为了高效的标记可被回收的内存块。 三色标记(Tri-color Marking)作为工具来辅助推导,把遍历对象图过程中遇到的对象,按照“是否访问过”这个条件标记成以下三种颜色: 白色:表示对象尚未被垃圾收集器访问过。 误标 什么是误标? 漏标和多标 对于错标其实细分出来会有两种情况,分别是:漏标和多标 多标-浮动垃圾 如果标记执行到 E 此刻执行了 object.E = null 在这个时候, E/F/G 理论上是可以被回收的。 三色标记法与垃圾回收器 增量更新:CMS 原始快照(STAB):G1,Shenandoah 参考文档 https://www.jianshu.com/p/12544c0ad5c1 https://hllvm-group.iteye.com
三色标记法 2.1 基本算法 要找出存活对象,根据可达性分析,从 GC Roots 开始进行遍历访问,可达的则为存活对象(最终结果:A/D/E/F/G 可达): ? 而当需要支持并发标记时,即标记期间应用线程还在继续跑,对象间的引用可能发生变化,多标和漏标的情况就有可能发生。 SATB 破坏了条件一:【灰色对象断开了白色对象的引用】,从而保证了不会漏标。 增量更新破坏了条件二:【黑色对象重新引用了该白色对象】,从而保证了不会漏标。 4. 三色标记法与现代垃圾回收器 现代追踪式(可达性分析)的垃圾回收器几乎都借鉴了三色标记的算法思想,尽管实现的方式不尽相同:比如白色/黑色集合一般都不会出现(但是有其他体现颜色的地方)、灰色集合可以通过栈/
为了能解释清楚这个问题,大佬们引入了三色标记法(Tri-color Marking)这个工具 需要注意的是,三色标记法只是辅助我们分析的工具,并不是某个垃圾收集器具体使用的算法!!!!! 更不是降低 STW 时间 or 消除 STW 的方法,具体解决方法下面还会介绍 在这里,三色标记法可以帮助我们搞清楚在可达性分析的第二阶段(也就是遍历对象图),如果用户线程和垃圾收集线程同时进行,会出现什么问题 辅助分析的工具:三色标记法 所谓三色标记法,就是把遍历对象图过程中遇到的对象,按照 “是否访问过” 这个条件标记成以下三种颜色: 白色:表示对象尚未被垃圾收集器访问过。 下面我们就用三色标记法来分析下,如果在对象图遍历这个阶段用户线程与收集器并发工作会出现什么问题 问题 1:浮动垃圾 所谓浮动垃圾,就是由于垃圾收集和用户线程是并行的,这个对象实际已经死亡了,已经没有其他人引用它了 ,会出现什么问题,的这么一个工具方法 所谓三色标记法,就是把遍历对象图过程中遇到的对象,按照 “是否访问过” 这个条件标记成以下三种颜色: 白色:表示对象尚未被垃圾收集器访问过。
漏标解决方案 正如前面所说,三色标记算法会造成漏标和多标问题。但多标问题相对不是那么严重,而漏标问题才是最严重的。 三色标记算法是什么? 三色标记算法是根可达算法的一种实现方案,其目的是为了找出所有可达对象。 为什么要有三色标记算法? 三色标记算法有什么缺陷? 三色标记算法会产生多标和漏标问题,其中漏标问题最严重。漏标问题会导致本该存活的对象被回收,从而导致严重的程序问题。 漏标有什么解决方案? 三色标记的漏标问题及两种解决方案_小幻_159 的博客 - CSDN 博客_三色标记漏标 三色标记法:多标与漏标 - 爱代码爱编程 三色标记!!!12. GC 中的 三色标记法_骚人贵的博客 - CSDN 博客_gc 三色标记 三色标记法:多标与漏标_朱四龙的博客 - CSDN 博客_三色标记漏标
那么后来就有了并发标记,适用于CMS和G1,并发标记的意思就是可以在不暂停用户线程的情况下对其进行标记,那么实现这种并发标记的算法就是三色标记法,三色标记法最大的特点就是可以异步执行,从而可以以中断时间极少的代价或者完全没有中断来进行整个 三色为那三色? 白色:尚未被GC访问过的对象,如果全部标记已完成依旧为白色的,称为不可达对象,既垃圾对象。 黑色:本对象已经被GC访问过,且本对象的子引用对象也已经被访问过了。 多标-浮动垃圾 一个本应该是垃圾的对象被视为了非垃圾,它的影响并不会很大,因为哪怕此次不会被回收下一次也会被回收 2. 然后在重新标记阶段,再以这些引用关系中的黑色对象为根,再扫描一次,以此保证不会漏标。 在记录下来之后会直接将它变为黑色,标为不需要处理,在实际清理的时候如果有对象引用它则正常,如果没有则为浮动垃圾,在下一次回收时会清除掉,但是此方案会产生较多的浮动垃圾。
深入浅出JVM(十六)之三色标记法与并发可达性分析”好事“这里推荐一篇MySQL优化相关的文章:深入探索SQL优化:利用慢查询日志和explain提升数据库效率 文章通过分析explain详细字段内容进行分析不同 ,最终总结出多条优化好习惯上篇文章深入浅出JVM(十五)之垃圾回收器(上篇)介绍性能指标吞吐量和延迟、串行收集器、并行收集器以及吞吐量优先收集器为了更好的描述并发垃圾收集器,本篇文章将先深入浅出的介绍三色标记法以及并发可达性分析遇到的问题以及解决方案三色标记法 finalize方法或者finalize方法搭不上引用链时才能回收这些对象不理解如何判断对象不再使用的同学可以看这篇文章深入浅出JVM(十一)之如何判断对象“已死”为了更好的说明并发可达性分析,我们使用三色标记法模拟 ,先说明三色标记法三色标记法的颜色、流程说明三色标记法的三种颜色说明黑色:当前对象被扫描过,并且它引用的对象也被扫描灰色:当前对象被扫描过,至少有一个引用未被扫描白色:当前对象未被扫描过GC 根节点总是黑色的 、并发可达性分析可能出现的浮动垃圾,对象丢失问题以及解决对象丢失问题的增量更新、原始快照两种方式在三色标记法中,黑色代表已经扫描完成、灰色代表至少有一个引用未扫描、白色代表还未扫描,GC根节点默认黑色隐式可达三色标记法从
“贴标签不手忙脚乱”:三色标记法 为了在"边营业边盘点"时不出错(比如刚标记完,顾客又把商品换了位置),保洁员用了三个颜色的标签: 白色:还没检查的商品 灰色:正在检查的商品(可能还有关联的商品没查) 保洁员先把顾客手里正拿着的商品(根对象)标为灰色,然后逐个检查灰色商品关联的其他商品(比如礼盒里的赠品),都标完后再把灰色改成黑色。最后剩下的白色商品,就是没人要的垃圾。 这个过程中如果有新商品上架(程序创建新对象),会直接标为灰色,保证不会被误当成垃圾清走。 4. “防止偷偷换东西”:写屏障 万一保洁员刚标完黑色,有顾客偷偷把商品换了(程序修改了对象引用)怎么办? 总结 Go 的垃圾回收机制核心是自动识别并回收不再使用的内存,其原理基于并发标记 - 清除算法:首先通过 “三色标记法” 追踪对象引用 —— 初始所有对象为白色(未检查),从根对象开始遍历,将可达对象标记为灰色
三色标记法是一种垃圾回收法,它可以让JVM不发生或仅短时间发生STW(Stop The World),从而达到清除JVM内存垃圾的目的。 JVM中的CMS、G1垃圾回收器所使用垃圾回收算法即为三色标记法。 三色标记算法思想 三色标记法将对象的颜色分为了黑、灰、白,三种颜色。 白色:该对象没有被标记过。 ,重新标记也不会从黑色对象中去找,导致该对象被程序所需要,却又要被GC回收,此问题会导致系统出现问题,而CMS与G1,两种回收器在使用三色标记法时,都采取了一些措施来应对这些问题,CMS对增加引用环节进行处理 解决办法 在JVM虚拟机中有两种常见垃圾回收器使用了该算法:CMS(Concurrent Mark Sweep)、G1(Garbage First) ,为了解决三色标记法对对象漏标问题各自有各自的法: CMS解决办法:增量更新 在应对漏标问题时,CMS使用了增量更新(Increment Update)方法来做: 在一个未被标记的对象(白色对象)被重新引用后,引用它的对象若为黑色则要变成灰色,在下次二次标记时让
并发标记算法(三色标记法) CMS和G1在并发标记时使用的是同一个算法:三色标记法,使用白、灰、黑三种颜色标记对象。白色是未标记;灰色自身被标记,引用的对象未标记;黑色自身与引用对象都已标记。 ? 2.4.5 漏标问题 在remark过程中,黑色指向了白色,如果不对黑色重新扫描,则会漏标。会把白色D对象当作没有新引用指向从而回收掉。 ? 并发标记过程中,Mutator删除了所有从灰色到白色的引用,会产生漏标。 此时白色对象应该被回收 产生漏标问题的条件有两个: 1.黑色对象指向了白色对象 2.灰色对象指向白色对象的引用消失 所以要解决漏标问题,打破两个条件之一即可: 1.跟踪黑指向白的增加 incremental
最终标记(Final Marking):对用户线程做一个短暂的暂停,用于处理并发标记阶段仍遗留下来的最后那少量的SATB记录(漏标对象)。 三色标记 在三色标记法之前有一个算法叫Mark-And-Sweep(标记清除)。这个算法会设置一个标志位来记录对象是否被使用。 所以就需要一个算法来解决GC运行时程序长时间挂起的问题,那就是三色标记法。 三色标记最大的好处是可以异步执行,从而可以以中断时间极少的代价或者完全没有中断来进行整个GC。 三色标记法很简单。 漏标问题 假设此时,对象A及其引用的对象都已经被扫描完,那么对象A将会被标记为黑色。 用户线程将对象B和对象C之间的引用断开,将对象A指向对象C,此时对象C会被当成垃圾对象,会产生漏标问题,因为对象A不会再被扫描。 漏标问题在CMS和G1收集器中有着不同的解决方案。
Go V1.5的三色并发标记法 所谓三色标记法其实就是用三种不同的颜色(灰白黑)来标记各个对象的状态,最后统一回收白色对象,保留黑色对象(灰色对象为过渡态)的方式。让我们来看一看具体过程。 三色标记法的步骤 每次新建的对象,默认全部都是白色 右边的标记表其实就是三种不同颜色的集合,被标记成哪种颜色,则对象就在哪个集合中。 我们从三色标记法的过程不难看出,里面会有很多并发流程均会被扫描,执行并发流程的内存可能存在相互依赖。 然后我们按照三色标记法的计算逻辑执行下去,将所有灰色对象标记为黑色,那么2和7就会被被标记为黑色,如图所示 然后白色对象会被全部清除,剩下黑色对象。 GoV1.5- 三色标记法, 堆空间启动写屏障,栈空间不启动,全部扫描之后,需要重新扫描一次栈(需要STW),效率普通 GoV1.8-三色标记法,混合写屏障机制, 栈空间不启动,堆空间启动。
•代表语言:Golang(三色标记法)•优点:解决了引用计数的缺点。•缺点:需要 STW,暂时停掉程序运行。 •代表语言:Java•优点:回收性能好•缺点:算法复杂 Golang 垃圾回收 跳过原理,我们先来介绍 Golang 的三色标记法。 三色标记法 三色标记法只是为了叙述方便而抽象出来的一种说法,实际上的对象是没有三色之分的。 这里的三色,对应了垃圾回收过程中对象的三种状态: •灰色:对象还在标记队列中等待•黑色:对象已被标记,gcmarkBits 对应位为 1 -- 该对象不会在本次 GC 中被回收•白色:对象未被标记,gcmarkBits 三色标记法 回收原理 通过上图,应该对三色标记法有了一个比较直观的了解,那么我们现在来讲讲原理。
标记-清除法的缺点 在标记之前会有一个STW(stop the world)暂停操作,让程序暂停,程序会出现卡顿(最重要的缺点) 标记需要扫描整个heap 清除数据会产生heap碎片 Go V1.5 三色标记法 三色标记法在GC中统计三个表,分别是White白色标记表、Grey灰色标记表以及Black黑色标记表。 STW的 在没有STW的情况下,三色标记法最不希望发生的事: 一个白色对象被黑色对象引用(白色对象被挂在黑色对象下) 这个白色对象与一个灰色对象间的可达关系遭到破坏(即有一个灰色丢弃了这个被黑色对象引用的白色 强三色不变式 即强制性的不允许黑色对象引用白色对象来破坏条件1 弱三色不变式 黑色对象可以引用白色对象,但白色对象存在其他灰色对象对它的引用,或者可达它的链路上游存在灰色对象 来破坏条件2 在三色标记中如果满足强 Go V1.8的三色标记法+混合写屏障机制 具体操作 GC开始将栈上的对象全部扫描标记为黑色(之后不再进行第二次重复扫描,无需STW) GC期间,任何在栈上创建的新对象,均为黑色。
三色标记法原始<标记-清除>算法的不足原始的Mark-Sweep算法流程上相对简单,但是在实际应用中有一定的限制条件。 三色标记法三色标记算法将所有的内存对象抽象为黑、白、灰三类。白色: 初始色,如果标记阶段结束还是白色,则该内存对象将被回收。 三色标记法只能由**白->灰->黑**单调变色用三色波面图来描述:图片三色标记法本身仍然不能保证并发和增量执行的正确性。在并发和增量执行的场景下,活跃对象(白色)被错误回收的必要条件:1. (否则结合1的话,该白色对象本来就是一个需要被回收的垃圾对象)但是三色标记法让并发和增量执行的实现变得容易。回到<标记-清除>中的异常例子。 标记-清除>算法和<三色标记法> 本身都不能保证在并发和增量执行的场景下垃圾回收的正确性,那么引入<三色标记法>的优势是什么,我们还需要结合屏障技术来看。
三、三色标记法 (一)原始<标记-清除>算法的不足 原始的Mark-Sweep算法流程上相对简单,但是在实际应用中有一定的限制条件。 (二)三色标记法 三色标记算法将所有的内存对象抽象为黑、白、灰三类。 白色: 初始色,如果标记阶段结束还是白色,则该内存对象将被回收。 三色标记法只能由白->灰->黑单调变色 用三色波面图来描述: 三色标记法本身仍然不能保证并发和增量执行的正确性。 (否则结合1的话,该白色对象本来就是一个需要被回收的垃圾对象) 但是三色标记法让并发和增量执行的实现变得容易。回到<标记-清除>中的异常例子。 标记-清除>算法和<三色标记法> 本身都不能保证在并发和增量执行的场景下垃圾回收的正确性,那么引入<三色标记法>的优势是什么,我们还需要结合屏障技术来看。
根据三色标记法将对象标记为黑色、灰色、白色;回收标为白色的对象,使其可以被再次利用。 三色标记法 所有对象初始状态都是白色; 从根节点开始扫描,并将引用对象标成灰色; 遍历灰色节点,将新遍历到的白色节点标记为灰色,并把上一步标记的灰色节点标记为黑色; 重复上面步骤,直到没有灰色节点 写屏障是在内存进行写操作之前执行的,一般需要满足以下两个原理: 强三色不变式,强制性的不允许黑色对象引用白色对象; 弱三色不变式,黑色对象可以引用的白色对象是,有其他灰色对象对它的直接引用,或者它的链路上游存在灰色对象 插入写屏障,引入新的白色对象时,就将白色对象标记为灰色,满足强三色不变式。处于性能和实现复杂度的考虑,go对栈空间没有使用写屏障,导致新增的引用对象无法及时发现。 go的垃圾回收是基于三色标记法,通过合理的使用内存屏障,大大较少了垃圾回收的STW。
https://cloud.tencent.com/developer/article/1730306https://cloud.tencent.com/developer/article/1764009三色标记算法 GC 线程和业务线程同时工作,在并发标记中,三色标记算法会存在两个缺陷:多标(浮动垃圾)、漏标。 其实B、C、D都已经是垃圾对象,但是本次三色标记被标记成了黑色,黑色代表存活对象,所以本次GC不会将B、C、D回收掉,B、C、D就是浮动垃圾。 2、漏标漏标:是指那些本该存活的对象,在一次GC回收过程中却被当做垃圾对象回收了 产生漏标需要两个必要条件,缺一不可: 1、黑色对象 -> 白色对象建立链接 2、灰色对象 -> 白色对象引用断开产生漏标的过程 ,正在标记 C第二步:业务线程:A -> D 新建了引用关系,同时 B -> D 的引用失效第三步:GC 线程:因为A已经扫描完成,所以未感知到 A -> D 的建立引用,认为没有任何引用指向D,D漏标被回收漏标影响