关于垃圾回收算法,基本就是那么几种:标记-清除、标记-复制、标记-整理。在此基础上可以增加分代(新生代/老年代),每代采取不同的回收算法,以提高整体的分配和回收效率。

无论使用哪种算法,标记总是必要的一步。你不先找到垃圾,怎么进行回收?今天一起看下三色标记法。

先看一下知识点导图:

一、如何标记

在 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开始,就会导致漏标的情况发生。

漏标的问题满足两个条件:

  1. 有至少一个黑色对象在自己被标记之后指向了这个白色对象
  2. 所有的灰色对象在自己引用扫描完成之前删除了对白色对象的引用

只有当上面两个条件都满足,三色标记算法才会发生漏标的问题。换言之,如果我们破坏任何一个条件,这个白色对象就不会被漏标。

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

垃圾回收之三色标记法相关推荐

  1. 内存管理篇(三):Go垃圾回收之三色标记算法

    三色标记法介绍: 三色标记法(tricolor mark-and-sweep algorithm)是传统 Mark-Sweep 的一个改进,它是一个并发的 GC 算法,在Golang中被用作垃圾回收的 ...

  2. JVM垃圾回收——三色标记法

    目录 一.什么是三色标记 二.三色标记的过程 三.三色标记的缺点 四.垃圾回收机如何弥补三色标记的缺点 在CMS.G1这种并发的垃圾收集器收集对象时,假如一个对象A被GC线程标记为不可达对象,但是用户 ...

  3. 【七天入门Go语言】 GC垃圾回收三色标记 | 第七天

    目录 GC 三色标记法 最后 GC GC全称Garbage Collection 目前主流的垃圾回收算法有两类,分别是追踪式垃圾回收算法(Tracing garbage collection)和引用计 ...

  4. 图文结合,白话 Go 的垃圾回收原理

    前面两篇文章介绍了Go语言的内存分配策略以及Go协程动态扩充和收缩栈内存的原理,今天这篇文章我们主要来聊一下内存管理的另外一大块内容:垃圾回收. 下面首先我们会聊一下什么是GC (垃圾回收),GC的作 ...

  5. 图文结合,白话Go的垃圾回收原理

    前面两篇文章介绍了Go语言的内存分配策略以及Go协程动态扩充和收缩栈内存的原理,今天这篇文章我们主要来聊一下内存管理的另外一大块内容:垃圾回收. 下面首先我们会聊一下什么是GC (垃圾回收),GC的作 ...

  6. JVM垃圾回收——G1垃圾收集器

    目录 一.什么是G1垃圾收集器 二.G1垃圾收集器的内存划分 三.G1垃圾收集器的收集过程 四.G1收集器的优缺点 五.G1收集器的JVM参数配置 一.什么是G1垃圾收集器 Garbage First ...

  7. 简明易懂的JVM垃圾回收理解

    文章目录 写在前面 垃圾回收(GC)的概念 垃圾回收判断 垃圾回收区域 对象是否可以被回收 垃圾回收搜索根 GC Roots 安全点 Safepoint 垃圾回收的时刻 垃圾回收算法 标记-清除算法 ...

  8. 【JVM进阶之路】垃圾回收机制和GC算法之三色标记(三)

    JVM往期文章 [JVM进阶之路]内存结构(一) [JVM进阶之路]玩转JVM中的对象(二) 上篇文章中讲到JVM中的对象以及判断对象的存活,那么对于"已死"的对象应该如何处理,怎 ...

  9. golang 垃圾回收、三色标记法、写屏障

    垃圾回收简称 GC,就是对程序中不再使用的内存资源进行自动回收释放的操作. 常见的垃圾回收算法 引用计数:每个对象维护一个 引用计数,当对象被创建或被其他对象引用时,计数 +1:如果引用该对象的对象被 ...

最新文章

  1. 小程序一次性上传多个本地图片,上拉加载照片以及图片加载延迟解决之道
  2. 判断某个点是否在不规则图形内
  3. python与php8-php8的扩展arginfo生成工具及工具初体验
  4. mysql中RAND()随便查询记录效率问题和解决的方法分享
  5. Python进阶01 词典
  6. get中添加header
  7. Sql Server之旅——第十二站 对锁的初步认识
  8. 【HDU - 1561】The more, The Better(树形背包,dp,依赖背包问题与空间优化,tricks)
  9. sigmoid函数_深度学习中激活函数总结
  10. imageio读取数据后需要关闭吗_关闭汽车自动启停功能后,能更换为普通汽车电瓶吗?有什么影响吗...
  11. 电脑C盘软件搬家,一键解决C盘不够用问题!
  12. 拼多多sdk php,标签PHP拼多多SDK文章 - 零分博客 - 关注互联网且乱扯淡互联网的个人博客...
  13. 三位物理学家与陶哲轩证明的惊天定理,原来早在教科书里吗?
  14. Arcgis中怎样设置调查路线线型(带箭头的虚线),附带1:1万地形图符号库
  15. python如何表示正整数_python中正整数
  16. C语言经典问题11-牛顿法求一个数的平方根函数
  17. GHOST重装系统详解
  18. 存储IP地址,到底用什么数据类型相对比较好
  19. 人工智能AI程序设计语言
  20. RabbitMQ:镜像队列Mirrored queue

热门文章

  1. 缓存的使用以及出现的问题
  2. 英特尔第十代处理器为什么不支持win7_Intel的7代酷睿CPU可以安装Win7系统吗?能不能完美支持呢?为什么有人说不能使用Win7系统...
  3. linux驱动面试题目汇总
  4. 在线教育系统网页版,观看视频直播功能更多,更适合用户学习
  5. 电脑怎么安装excel表格
  6. ​新手网赚要避的几个坑,你知道吗?
  7. double转换为二进制
  8. EARTH DATA网站注册走过的坑
  9. 重磅!镭速传输Raysync发布11项全新功能
  10. 【论文】Saliency Detection: A Spectral Residual Approach阅读笔记