当前位置: 首页 > news >正文

深入解析三色标记算法

引言

在现代垃圾回收器中,并发标记是实现低延迟目标的关键技术。而三色标记算法则是并发标记的理论模型和实现基础。它巧妙地通过三种颜色来模拟对象在标记过程中的状态,从而解决了在用户线程与垃圾收集线程并发执行时带来的复杂性问题。

一、核心思想:为何需要三色标记?

传统的“标记-清除”算法在标记时必须暂停所有用户线程,以防止对象引用关系在标记过程中发生变化。而三色标记法的核心目标,就是允许垃圾收集线程与用户线程并发执行,从而极大地减少垃圾收集时的停顿时间。

它将对象抽象为三种颜色,来表征其在标记过程中的状态:

1. 白色对象

表示对象尚未被垃圾收集器访问过。在回收开始时,所有对象都是白色;在回收结束时,任何仍然是白色的对象,即被视为不可达,可以被回收。

2. 灰色对象

表示对象已经被垃圾收集器访问过,但其至少还有一个引用的字段未被访问(即该对象直接引用的对象还没有被扫描)。灰色对象是“待扫描”的对象。

3. 黑色对象

表示对象已经被垃圾收集器访问过,并且其所有直接引用的字段也都已经被访问过了。黑色对象是“已扫描”的对象,代表它是存活的,并且不会在此次收集中被再次扫描。

核心不变式任何黑色对象都不会直接指向白色对象这意味着,一旦一个对象被标记为黑色,它就不会再依赖任何白色对象(即不会被错误回收)。

二、工作流程:标记如何推进?

三色标记的过程可以看作是一个对象颜色从白到灰再到黑的演变过程。

  1. 初始阶段

    • 从GC Roots开始遍历。

    • 所有GC Roots直接可达的对象被标记为灰色,并放入一个专门的“待扫描集合”。

    • 其余所有对象均为白色

  2. 标记阶段

    • 根对象标记:垃圾回收器从GC Roots根对象(如栈中的引用、静态变量引用等)开始扫描,将根对象标记为灰色。

    • 灰色对象处理:依次处理每个灰色对象,将其引用的所有白色对象标记为灰色,并将该灰色对象自身标记为黑色。

    • 循环处理:重复上述步骤,直到所有灰色对象都变为黑色对象为止。

  3. 回收阶段

    • 标记阶段结束后,所有存活的对象都已经被标记为黑色

    • 所有剩余的白色对象即为不可达的垃圾对象,可以被安全地回收。

三、并发带来的挑战:漏标问题

在并发标记过程中,由于用户线程和垃圾回收线程同时运行,可能会出现以下两种情况,导致对象被错误地标记为垃圾:

  • 条件一:一个白色对象(未扫描的对象)被黑色对象(已扫描的对象)引用。
  • 条件二:从灰色对象到该白色对象(未扫描的对象)的直接或间接引用被破坏。

当这两个条件同时满足时,由于黑色对象不会被再次扫描,而且灰色对象也无法引用这个白色对象,所以会导致白色对象被错误地标记为垃圾,这种现象称为"对象消失问题",也叫漏标。

四、解决方案:如何防止漏标?

1. 增量更新 - 破坏条件1

  • 思想:关注新增的引用。如果用户线程试图让一个黑色对象引用一个白色对象,JVM会通过写操作将这个新插入的引用记录下来。

  • 行为:在写操作中,会将这个新被引用的白色对象(C)直接标记为灰色,并重新放入“待扫描集合”。

  • 效果:破坏了漏标的第一个条件。黑色对象A指向了白色对象C,但C立刻被变成了灰色,保证了它会被后续扫描到,从而不会漏掉。

  • 代表收集器CMS 收集器在并发标记阶段就使用了增量更新算法。

2. 原始快照 - 破坏条件2

  • 思想:关注删除的引用。在并发标记开始时,逻辑上为对象图创建一个快照。如果用户线程要删除一个灰色对象到白色对象的引用(即 B → C),JVM会通过写屏障将这个即将被删除的引用关系记录下来

  • 行为:在写屏障中,会将被删除引用指向的那个白色对象(C)标记为灰色

  • 效果:破坏了漏标的第二个条件。即使删除了 B → C,C也被变成了灰色,它仍然会被视为快照中的存活对象而被继续扫描。如果之后有新的引用指向它(如 A → C),它自然就是存活的。

  • 代表收集器G1 收集器采用了原始快照算法。


文章转载自:

http://q7GdJl7F.qfLgn.cn
http://jTEeaDiP.qfLgn.cn
http://l44mIjwc.qfLgn.cn
http://GAIqT0Wn.qfLgn.cn
http://Dxeeq2vA.qfLgn.cn
http://DXZnhiMv.qfLgn.cn
http://CeWiP2DO.qfLgn.cn
http://7kpGByYf.qfLgn.cn
http://oV3JvVzL.qfLgn.cn
http://R7UTY1UO.qfLgn.cn
http://CrDxh0fl.qfLgn.cn
http://H0GrJxEw.qfLgn.cn
http://knWrcyib.qfLgn.cn
http://u2YDh3lP.qfLgn.cn
http://MFPvmZnS.qfLgn.cn
http://z6If5MIr.qfLgn.cn
http://fGs9ODdU.qfLgn.cn
http://jOE0drfA.qfLgn.cn
http://Aa5JbqTv.qfLgn.cn
http://HkmykxVx.qfLgn.cn
http://0H1MO0C2.qfLgn.cn
http://d0YvzaDM.qfLgn.cn
http://yd9TY1nQ.qfLgn.cn
http://JVNejN8a.qfLgn.cn
http://2126qOSl.qfLgn.cn
http://unjSY55l.qfLgn.cn
http://flYjwbO4.qfLgn.cn
http://2rWpt536.qfLgn.cn
http://J9j4WNnS.qfLgn.cn
http://o71CKSek.qfLgn.cn
http://www.dtcms.com/a/371607.html

相关文章:

  • Python struct模块 | 使用pack函数进行字节序打包
  • 二叉树的前中后序遍历(迭代法)
  • Camx-系统默认创建camxoverridesettings.txt
  • SQL面试题及详细答案150道(101-115) --- 数据操纵与定义篇
  • Adobe Premiere Pro(Pr)2022视频编辑软件安装教程与下载地址
  • 18.4 查看订单
  • 【考研C语言编程题】数组元素批量插入实现(含图示+三部曲拆解)
  • 九.弗洛伊德(Floyd)算法
  • pytorch非线性回归
  • Java 大视界 -- Java 大数据机器学习模型在金融市场风险评估与投资组合优化中的应用(407)
  • Python快速入门专业版(十一):布尔值与None:Python中的“真假”与“空值”(附逻辑判断案例)
  • 鸿蒙NEXT应用数据持久化全面解析:从用户首选项到分布式数据库
  • Linux笔记---封装套接字
  • 轻松Linux-8.动静态库的制作及原理
  • LeetCode 面试经典 150 题:移除元素(双指针思想优化解法详解)
  • 【TypeScript】闭包
  • 后端(fastAPI)学习笔记(CLASS 1):扩展基础
  • Spring Boot @RestController 注解详解
  • 腾讯云语音接口实现会议系统
  • ESP32与SUI-101A实现用电器识别
  • Wan2.2-S2V - 音频驱动图像生成电影级质量的数字人视频 ComfyUI工作流 支持50系显卡 一键整合包下载
  • 开始 ComfyUI 的 AI 绘图之旅-图生图(二)
  • VS2017安装Qt插件
  • ZYNQ FLASH读写
  • 容器元素的滚动条回到顶部
  • 【音频字幕】构建一个离线视频字幕生成系统:使用 WhisperX 和 Faster-Whisper 的 Python 实现
  • ncnn-Android-mediapipe_hand 踩坑部署实录
  • java面试中经常会问到的mysql问题有哪些(基础版)
  • SoundSource for Mac 音频控制工具
  • Unity学习----【进阶】Input System学习(一)--导入与基础的设备调用API