垃圾回收之三色标记法
关于垃圾回收算法,基本就是那么几种:标记-清除、标记-复制、标记-整理。在此基础上可以增加分代(新生代/老年代),每代采取不同的回收算法,以提高整体的分配和回收效率。
无论使用哪种算法,标记总是必要的一步。你不先找到垃圾,怎么进行回收?今天一起看下三色标记法。
先看一下知识点导图:
一、如何标记
在 GC 领域里,判断对象存活的主流思路是两个,「引用计数」和「可达性分析」。
1、引用计数
顾名思义,引用计数的思路就是给每个对象进行计数,每被其它对象引用一次,计数就 +1,引用失效后,计数就 -1。当计数器的数值为 0,就意味着它没有被使用,可以回收。
2、可达性分析
可达性分析的思路就是通过引用链路判断对象是否可被触达,如果能触达说明该对象当前正在被使用,不可回收;反之,没有触达到的对象则认为是无使用的,可以回收。
这个引用链路的结构类似于有向有环图,但是根节点不止一个,是一个集合,称之为 GCRoots。
目前主流的 GC 机制大多用的是「可达性分析」这条路线。
为什么引用计数不好用呢?因为它有一个特别严重的问题:无法处理循环引用。
像上图这样的情况,引用计数永远不为 0,这些对象就永远不会被回收。
二、常规标记-清除
常规的标记清除严格按照追踪式算法的思路来实现的。这个算法会设置一个标志位来记录对象是否被使用。最开始所有的标记位都是 0,如果发现对象是可达的就会置为 1,一步步下去就会呈现一个类似树状的结果。
等标记的步骤完成后,会将未被标记的对象统一清理,再次把所有的标记位设置成 0 方便下次清理。
标记清除法主要包含两个步骤:
- 标记
- 清除
示例如下:
1、开启STW,停止程序的运行,图中是本次GC涉及到的root节点和相关对象。
2、从根节点出发,标记所有可达对象。
3、停止STW,然后回收所有未被标记的对象
这样执行整个GC期间需要STW,将整个程序暂停。因为如果不进行STW的话,会出现已经被标记的对象A,引用了新的未被标记的对象B,但由于对象A已经标记过了,不会再重新扫描A对B的可达性,从而将B对象当做垃圾回收掉的问题。
三、三色标记
垃圾收集器依据可达性分析算法判断对象是否存活时,将遍历GC Roots过程中遇到的对象,按照“是否访问过”这个条件,把对象标记成白色(white)、灰色(gray)、黑色(black)三种颜色,这个标记过程称为三色标记法。
相比传统的标记清扫算法,三色标记最大的好处是可以异步执行,从而可以以中断时间极少的代价或者完全没有中断来进行整个 GC。
1、基本算法
三色标记法将对象用三种颜色表示,分别是白色、灰色和黑色。
最开始所有对象都是白色的,然后把其中全局变量和函数栈里的对象置为灰色。
第二步把灰色的对象全部置为黑色,然后把原先灰色对象指向的变量都置为灰色,以此类推。
等发现没有对象可以被置为灰色时,所有的白色变量就一定是需要被清理的垃圾了。
- 初始标记阶段,指的是标记 GCRoots 直接引用的节点,将它们标记为灰色,这个阶段需要 「Stop the World」。
- 并发标记阶段,指的是从灰色节点开始,去扫描整个引用链,然后将它们标记为黑色,这个阶段不需要「Stop the World」。
- 重新标记阶段,指的是去校正并发标记阶段的错误,这个阶段需要「Stop the World」。
- 并发清除,指的是将已经确定为垃圾的对象清除掉,这个阶段不需要「Stop the World」。
三色标记法是一个 false negative(假阴性)的算法:
- 三色标记法因为多了一个白色的状态来存放不确定的对象,所以可以异步地执行。
- 当然异步执行的代价是可能会造成一些遗漏,因为那些早先被标记为黑色的对象可能目前已经是不可达的了。
2、现代垃圾回收器实现
现代追踪式(可达性分析)的垃圾回收器几乎都借鉴了三色标记的算法思想,尽管实现的方式不尽相同:比如白色/黑色集合一般都不会出现(但是有其他体现颜色的地方)、灰色集合可以通过栈/队列/缓存日志等方式进行实现、遍历方式可以是广度/深度遍历等等。
对于读写屏障,以Java HotSpot VM 为例,其并发标记时对漏标的处理方案如下:
- CMS:写屏障 + 增量更新
- G1:写屏障 + SATB
- ZGC:读屏障
四、多标及漏标问题
三色标记算法缺陷:在并发标记阶段的时候,因为用户线程与GC线程同时运行,有可能会产生多标或者漏标;
- 多标--多标记(浮动垃圾)
- 漏标--漏标记
1、多标问题
并发标记:用户与GC线程同时运行,假设现在扫描到C对象,B对象变为黑色,用户线程执行C的属性E=null,GC线程扫描C对象引用链,认为E对象是为可达对象,但是C对象根本没有引入到E对象,E对象应该是为垃圾对象,这种问题,可以在重新标记阶段(修正)修复。
并发清除阶段:用户与GC线程同时运行,会产生新的对象但是没有及时被GC清理。
多标只能在下一次GC清理垃圾的修复。
2、漏标问题
1.用户线程先执行C的E属性=null;GC线程的GcRoot就扫描不到E。Gc就认为E对象就是为垃圾对象,不可达对象。
2.用户线有执行B.E属性=E;E对象就是应该是为可达对象。
3.因为GCRoot是从C开始,不会从黑色的B开始,就会导致漏标的情况发生。
漏标的问题满足两个条件:
- 有至少一个黑色对象在自己被标记之后指向了这个白色对象
- 所有的灰色对象在自己引用扫描完成之前删除了对白色对象的引用
只有当上面两个条件都满足,三色标记算法才会发生漏标的问题。换言之,如果我们破坏任何一个条件,这个白色对象就不会被漏标。
CMS如何解决漏标问题---写屏障+增量更新方式
满足一个条件(灰色对象与白色对象断开连接),在并发标记阶段当我们黑色对象(B)引用关联白色对象(E),记录下B黑色对象。
在重新标记阶段(所有用户线程暂停),有将B对象变为灰色对象将整个引用链全部扫描。
缺点:遍历B整个链的效率非常低,有可能会导致用户线程等待的时间非常长。
G1如何解决漏标问题---原始快照方式
在C断开E的时候,会记录原始快照,在重新标记阶段的时候以白色对象变为灰色为起始点扫描整个链,本次GC是不会被清理。
好处:如果假设B(黑色对象)引入该白色对象的时候,无需做任何遍历效率是非常高。
缺点:如果假设B(黑色对象) 没有引入该白色对象的时候,该白色对象在本次GC继续存活,只能放在下一次GC在做并发标记的时候清理。
tips:以浮动垃圾(占内存空间)换让我们用户线程能够暂停的时间更加短。
总结:
对于读写屏障,以Java HotSpot VM为例,其并发标记时对漏标的处理方案如下:
- CMS:采用的是写屏障 + 增量更新
- G1: 采用的是写屏障 + 原汁快照(SATB)
- ZGC:采用的是读屏障
CMS收集器解决漏标问题:增量方式 如果现在B(黑色)对象引入白色对象,写屏障。
好处:避免浮动垃圾,缺点扫描整个引用链效率比较低。
G1收集器解决漏标问题:原始快照方式。
好处:效率非常高,无需扫描整个引用链,缺点:可能会产生浮动垃圾。
参考资料:
https://en.wikipedia.org/wiki/Tracing_garbage_collection
https://www.cnblogs.com/jmcui/p/14165601.html
垃圾回收之三色标记法相关推荐
- 内存管理篇(三):Go垃圾回收之三色标记算法
三色标记法介绍: 三色标记法(tricolor mark-and-sweep algorithm)是传统 Mark-Sweep 的一个改进,它是一个并发的 GC 算法,在Golang中被用作垃圾回收的 ...
- JVM垃圾回收——三色标记法
目录 一.什么是三色标记 二.三色标记的过程 三.三色标记的缺点 四.垃圾回收机如何弥补三色标记的缺点 在CMS.G1这种并发的垃圾收集器收集对象时,假如一个对象A被GC线程标记为不可达对象,但是用户 ...
- 【七天入门Go语言】 GC垃圾回收三色标记 | 第七天
目录 GC 三色标记法 最后 GC GC全称Garbage Collection 目前主流的垃圾回收算法有两类,分别是追踪式垃圾回收算法(Tracing garbage collection)和引用计 ...
- 图文结合,白话 Go 的垃圾回收原理
前面两篇文章介绍了Go语言的内存分配策略以及Go协程动态扩充和收缩栈内存的原理,今天这篇文章我们主要来聊一下内存管理的另外一大块内容:垃圾回收. 下面首先我们会聊一下什么是GC (垃圾回收),GC的作 ...
- 图文结合,白话Go的垃圾回收原理
前面两篇文章介绍了Go语言的内存分配策略以及Go协程动态扩充和收缩栈内存的原理,今天这篇文章我们主要来聊一下内存管理的另外一大块内容:垃圾回收. 下面首先我们会聊一下什么是GC (垃圾回收),GC的作 ...
- JVM垃圾回收——G1垃圾收集器
目录 一.什么是G1垃圾收集器 二.G1垃圾收集器的内存划分 三.G1垃圾收集器的收集过程 四.G1收集器的优缺点 五.G1收集器的JVM参数配置 一.什么是G1垃圾收集器 Garbage First ...
- 简明易懂的JVM垃圾回收理解
文章目录 写在前面 垃圾回收(GC)的概念 垃圾回收判断 垃圾回收区域 对象是否可以被回收 垃圾回收搜索根 GC Roots 安全点 Safepoint 垃圾回收的时刻 垃圾回收算法 标记-清除算法 ...
- 【JVM进阶之路】垃圾回收机制和GC算法之三色标记(三)
JVM往期文章 [JVM进阶之路]内存结构(一) [JVM进阶之路]玩转JVM中的对象(二) 上篇文章中讲到JVM中的对象以及判断对象的存活,那么对于"已死"的对象应该如何处理,怎 ...
- golang 垃圾回收、三色标记法、写屏障
垃圾回收简称 GC,就是对程序中不再使用的内存资源进行自动回收释放的操作. 常见的垃圾回收算法 引用计数:每个对象维护一个 引用计数,当对象被创建或被其他对象引用时,计数 +1:如果引用该对象的对象被 ...
最新文章
- 小程序一次性上传多个本地图片,上拉加载照片以及图片加载延迟解决之道
- 判断某个点是否在不规则图形内
- python与php8-php8的扩展arginfo生成工具及工具初体验
- mysql中RAND()随便查询记录效率问题和解决的方法分享
- Python进阶01 词典
- get中添加header
- Sql Server之旅——第十二站 对锁的初步认识
- 【HDU - 1561】The more, The Better(树形背包,dp,依赖背包问题与空间优化,tricks)
- sigmoid函数_深度学习中激活函数总结
- imageio读取数据后需要关闭吗_关闭汽车自动启停功能后,能更换为普通汽车电瓶吗?有什么影响吗...
- 电脑C盘软件搬家,一键解决C盘不够用问题!
- 拼多多sdk php,标签PHP拼多多SDK文章 - 零分博客 - 关注互联网且乱扯淡互联网的个人博客...
- 三位物理学家与陶哲轩证明的惊天定理,原来早在教科书里吗?
- Arcgis中怎样设置调查路线线型(带箭头的虚线),附带1:1万地形图符号库
- python如何表示正整数_python中正整数
- C语言经典问题11-牛顿法求一个数的平方根函数
- GHOST重装系统详解
- 存储IP地址,到底用什么数据类型相对比较好
- 人工智能AI程序设计语言
- RabbitMQ:镜像队列Mirrored queue
热门文章
- 缓存的使用以及出现的问题
- 英特尔第十代处理器为什么不支持win7_Intel的7代酷睿CPU可以安装Win7系统吗?能不能完美支持呢?为什么有人说不能使用Win7系统...
- linux驱动面试题目汇总
- 在线教育系统网页版,观看视频直播功能更多,更适合用户学习
- 电脑怎么安装excel表格
- ​新手网赚要避的几个坑,你知道吗?
- double转换为二进制
- EARTH DATA网站注册走过的坑
- 重磅!镭速传输Raysync发布11项全新功能
- 【论文】Saliency Detection: A Spectral Residual Approach阅读笔记