首页 > 编程语言 >JVM垃圾回收并发标记阶段详解

JVM垃圾回收并发标记阶段详解

来源:互联网 2026-07-10 08:06:06

并发标记采用三色标记法作为逻辑骨架,通过写屏障防止并发修改导致的漏标风险。标记过程与用户线程并行,结束后需再标记阶段进行短暂STW,扫描写屏障记录的变化和GCRoots,确保标记完整性。

并发标记阶段——这是CMS、G1这些现代垃圾回收器实现“低停顿”承诺的核心武器。它的任务听起来不太复杂:在用户线程持续跑着的同时,把堆里哪些对象还活着准确找出来。但麻烦就在于,它不追求绝对的原子性,而是靠一套逻辑框架——三色标记法,再加一道写屏障(Write Barrier)来防住并发修改导致的漏标风险。说实话,把这个过程捋清楚了,才算真正懂了CMS和G1的设计精妙之处。

JVM垃圾回收并发标记阶段详解

长期稳定更新的攒劲资源: >>>点此立即查看<<<

三色标记作为并发标记的逻辑骨架

整个堆在逻辑上被划分成三类对象:白、灰、黑。这并非某个JVM内部的具体数据结构,而是一种抽象的染色模型——它帮着GC线程判断当前标记到哪里了、下一步该扫谁。

  • 白色对象:还没被GC访问过。初始时所有对象都是白的,标记结束还挂着白的,那对不住了——判定为垃圾。
  • 灰色对象:被GC访问过了,但它引用的子对象还没全部扫描完。这相当于当前扫描的“工作队列”,灰色集合就是推进的引擎。
  • 黑色对象:已经彻底扫描完毕,它直接引用的对象要么已经变灰、要么已经变黑。GC不会再重新扫黑色对象,而且黑色对象不能直接指向白色对象——否则标记完整性就崩了。

并发标记的典型执行流程

以CMS或G1为例,并发标记通常在初始标记(一个短暂的STW停顿)之后立即启动。具体怎么跑的呢?

  • 初始标记结束后,所有从GC Roots直接可达的对象被打上灰色,入队。
  • GC线程从灰色集合里取出一个对象,遍历它所有的引用字段,把每一个还没访问过的引用对象从白色染成灰色(如果已经是灰或黑就跳过),再把当前这个灰对象自己涂黑。
  • 这个过程和用户线程是并发的——用户线程该赋值赋值、该置null置null,对象图随时可能变样。
  • 等到灰色集合空了,本轮并发标记逻辑就完成了。但注意:此时的结果可能不够精确,因为并发修改带来了漏标隐患……所以后面还得补一手再标记。

为什么需要再标记?——并发导致的漏标问题

用户线程在并发标记期间,有两类操作可能破坏三色不变式,导致漏标:

  • 赋值器插入新引用:比如A→B,A已经变黑,B还是白的。如果B还没被扫描到,黑指向白这条边就冒出来了,B可能被漏掉。
  • 赋值器删除引用:A→B被断开,而A已经黑了、B还是白的,如果B没有其他路径可达,就成了一个悬空的白色存活对象——同样会漏标。

为了拦住这些变动,JVM在写操作(比如putfield)的前后插入了写屏障。写屏障会把被修改的引用或相关对象记到“卡表(Card Table)”或者“增量更新/原始快照(SATB)缓冲区”里,等再标记阶段集中扫描。

再标记阶段的作用与特点

再标记(Remark)是一个短暂的Stop-the-World阶段。它的活儿其实就两件:

  • 扫描写屏障留下的“脏卡”或SATB缓冲区,把里面涉及的对象及其子图重新跑一遍标记。
  • 重新扫描GC Roots(栈帧、JNI引用、全局变量等),确保所有新冒出来的根引用都被覆盖到。

由于绝大多数对象已经在并发标记阶段处理完毕,再标记只需要聚焦增量变化,所以STW暂停时间远短于初始标记。控制整体停顿的关键,恰恰就在这个环节的优化上。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备14008430号-1 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。