大聪明助你拿Offer | 三色标记算法了解吗?

前言

🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。
🍊支持作者: 点赞👍、关注💖、留言💌~

“面试造火箭,入职拧螺丝?” 别慌!这里没有“茴香豆的茴有几种写法”,只有最实用、最高频、最能唬住面试官的 Java 面试题解析!
你是不是也经历过:

  • 🤯 背了一堆八股文,面试官一问就懵圈?
  • 😅 明明知道答案,却讲得支支吾吾?
  • 🚀 想进大厂,却连 HashMap 和 ArrayList 的区别都说不利索?

别怕!这就是你的“Java面试急救包”,大聪明专治各种答不上来😉

正文

💡每日一题:三色标记算法了解吗?
💡难度系数: ⭐⭐⭐⭐⭐

关于垃圾回收算法,基本就是那么几种:标记-清除、标记-复制、标记-整理。在此基础上可以增加分代(新生代/老年代),每代采取不同的回收算法,以提高整体的分配和回收效率。当然,无论使用哪种算法,标记总是必要的一步。毕竟只有找到了垃圾才能进行回收~

垃圾回收器的工作流程大体如下:

  1. 标记出哪些对象是存活的,哪些是垃圾(可回收);
  2. 进行回收(清除/复制/整理),如果有移动过对象(复制/整理),还需要更新引用。

今天就跟各位小伙伴一起聊聊什么是三色标记算法🤔

💡三色标记算法

三色标记算法的思想是:根据可达性分析,从GC Roots开始进行遍历访问,可达的则为存活对象。

在这里插入图片描述
我们把遍历对象图过程中遇到的对象,按 “是否访问过” 这个条件标记成以下三种颜色:

  • 白色:尚未访问过。
  • 黑色:本对象已访问过,而且本对象引用到的其他对象也全部访问过了。
  • 灰色:本对象已访问过,但是本对象引用到的其他对象尚未全部访问完。全部访问后,会转换为黑色。

假设现在有白、灰、黑三个集合(表示当前对象的颜色),其遍历访问过程为:

  1. 初始时,所有对象都在 【白色集合】中;
  2. 将GC Roots 直接引用到的对象 挪到 【灰色集合】中;
  3. 从灰色集合中获取对象:
    3.1. 将本对象 引用到的 其他对象 全部挪到 【灰色集合】中;
    3.2. 将本对象 挪到 【黑色集合】里面。
  4. 重复步骤3,直至【灰色集合】为空时结束。
  5. 结束后,仍在【白色集合】的对象即为GC Roots 不可达,可以进行回收。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

⚠️注意:当 GC (垃圾回收)发生时进行标记或者进行回收时,都会进行“Stop The World”(STW),指的是系统在执行特定操作时需暂停(停止)所有应用程序线程。我们可以将其简单理解为在进行标记或者垃圾回收时,会对我们的程序进行短暂的暂停。进行暂停的原因也非常简单,比如我们家里有个扫地机器人在打扫卫生,此时你在嗑瓜子,那么你就需要先停一会(停止制造瓜子皮),这样才能让扫地机器人打扫干净,如果你一边嗑瓜子,扫地机器人一边打扫卫生,那么家里是打扫不干净的~ 当Stop The World 时,对象间的引用是不会发生变化的,可以轻松完成标记。虽然 STW 可以让标记变得更加轻松,但是会影响系统的性能,所以三色标记算法进行了一个升级,让标记阶段支持并发标记,即标记期间应用线程还在继续跑。虽然提升了系统的性能,但是对象间的引用可能发生变化,就导致可能出现多标漏标的情况。

💡 多标-浮动垃圾

假设已经遍历到E(变为灰色了),此时应用执行了 objD.fieldE = null :

在这里插入图片描述
此刻之后,对象E/F/G是“应该”被回收的。然而因为E已经变为灰色了,其仍会被当作存活对象继续遍历下去。最终的结果是:这部分对象仍会被标记为存活,即本轮GC不会回收这部分内存。这部分本应该回收但是没有回收到的内存,被称之为“浮动垃圾”。浮动垃圾并不会影响应用程序的正确性,只是需要等到下一轮垃圾回收中才被清除。

📍扩展:这也就是为什么 JVM 中老年代的内存占用达到 80%-90% 就开始执行垃圾回收,这样可以有效避免这些浮动垃圾占用内存。

💡 漏标-读写屏障
并发标记的过程中,一个业务线程将一个未被扫描过的白色对象断开引用成为垃圾(删除引用),同时黑色对象引用了该对象(增加引用),这两步可以不分先后顺序。因为黑色对象的含义为其属性都已经被标记过了,重新标记也不会从黑色对象中去找,导致该对象被程序所需要,却又要被 GC 回收,此问题会导致系统出现问题。目前较为主流的垃圾回收器CMS和G1,两种回收器在使用三色标记法时,都采取了一些措施来应对这些问题,CMS 对增加引用环节进行处理(Increment Update),G1 则对删除引用环节进行处理(SATB)。

在这里插入图片描述
不难分析,漏标只有同时满足以下两个条件时才会发生:

  • 用户线程将黑色对象引用指向白色对象
  • 用户线程将灰色对象与白色对象的引用断开

假如白色对象被黑色对象引用了,白色对象与灰色对象的引用没断,那么就没问题,因为迟早会被扫描标记到,而如果白色与灰色之间的引用关系断了,那就有问题了,这个白色对象将不会被扫描标记到,就会被漏掉,当成无用垃圾被回收掉。要解决漏标的问题处理方法就是在并发标记阶段破坏/记录任意一种情况即可,并发标记结束后再将这些漏标的对象重新标记作为根重新扫描标记一次。

增量更新(Incremental update) - CMS的做法

  • 破坏第一个条件,当有黑色对象新的引用白色对象时,就把插入的引用记录下来,
  • 等并发扫描过后,再讲这些记录的应用关系中的黑色对象作为跟,重扫一遍
  • 其实可以理解成黑色对象一旦插入了只想白色对象的引用后,就变为灰对色象了,因为要让它保证继续扫

原始快照(SATB) - G1的做法

  • 破坏第二个条件,要是有灰色要删除指向白色引用关系时,就将这个删除引用记录下来
  • 等并发扫描结束后,再将灰色对象为根,重扫一遍
  • 可以理解为记录删除引用,再以灰为根去扫

📍增量更新和原始快照都能保证所有活着的对象能被扫描上,但是可能还会把一些已经死了的垃圾扫描上,这样就会产生“浮动垃圾”。

小结

📢 面试不是终点,而是技术成长的起点!持续关注本专栏,更多硬核内容在路上! 🚀
💻PS:如果觉得有用,别忘了点赞+收藏,你的支持是我更新的最大动力!❤️

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不肯过江东丶

您的鼓励将是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值