深入理解JVM《G1垃圾收集器:面向局部收集与停顿模型的里程碑》

简介: G1收集器是JDK 9+默认的高性能垃圾回收器,采用Region分区模型,实现可预测停顿时间。它通过RSet跟踪跨区引用,结合SATB算法确保并发标记准确性,兼顾低延迟与高吞吐,适用于大内存多核场景。

G1(Garbage-First)收集器是垃圾收集技术发展史上的一个里程碑。它旨在替代CMS,成为服务端、大内存、多处理器场景下的默认收集器。从JDK 9开始,G1被确立为默认的垃圾收集器。

核心目标与设计理念

G1的核心设计目标是在延迟可控的情况下,尽可能提高吞吐量。与之前的收集器不同,G1摒弃了传统的连续物理分代(新生代、老年代)设计,而是采用了一种基于Region的堆内存布局可预测的停顿时间模型

革命性变化:Region分区模型

G1将整个Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以根据需要,扮演Eden空间Survivor空间Old空间。Region的大小可以通过 -XX:G1HeapRegionSize 设置,取值范围为1M到32M,且应为2的N次幂。

这种设计使得G1不再坚持固定大小和固定数量的分代划分。它能够将收集范围限制在多个Region之间,而无需针对整个新生代或老年代。这使得G1可以建立可预测的停顿时间模型:让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒(通过参数 -XX:MaxGCPauseMillis 指定,默认200ms)。

G1会跟踪各个Region里面的垃圾堆积的“价值”大小(即回收所能获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的那些Region。这就是“Garbage-First”名字的由来。

G1的执行过程

G1的运作过程大致可分为以下几个阶段,其中某些阶段与CMS类似。

1. 年轻代GC (Young GC)

  • 触发条件:当Eden区的Region被耗尽,无法为新对象分配空间时,G1会触发一次年轻代GC。
  • 过程
  • 根扫描:扫描GC Roots(STW)。
  • 更新RSet:处理 Dirty Card Queue 中的记录,更新 RSet(STW)。此阶段会完成对RSet的更新(详见下文)。
  • 处理RSet:识别被老年代对象指向的Eden区对象,这些对象是存活对象(STW)。
  • 对象拷贝:采用复制算法,将存活的对象从Eden区和From Survivor区拷贝到To Survivor区(一个新的Region),或者晋升(Promote) 到老年代的Region(STW)。
  • 清空Region:回收整个Eden区和已使用的Survivor区。
  • 特点:这是一个完全STW的并行收集过程,由多个GC线程同时完成。

2. 并发标记周期 (Concurrent Marking Cycle)

这是G1回收老年代Region的核心阶段,它是一个相对独立的过程,并非每次Young GC都会触发。其步骤与CMS非常相似但更为复杂:

a) 初始标记 (Initial Mark)

  • 任务:仅仅标记一下GC Roots能直接关联到的对象,并修改TAMS指针,为下一阶段并发标记做准备。
  • 状态:需要 STW
  • 技巧:这个阶段借用于一次正常的Young GC,因此没有额外的、明显的停顿成本。

b) 根区域扫描 (Root Region Scanning)

  • 任务:扫描在初始标记中标记为存活的对象所在的Survivor Region(根区域),这些对象可能引用了老年代的对象。
  • 状态并发执行,但必须在下次Young GC开始前完成,否则Young GC必须等待。

c) 并发标记 (Concurrent Marking)

  • 任务:从GC Roots开始,进行可达性分析,递归扫描整个堆里的对象图,找出所有存活的对象。
  • 状态并发执行。耗时最长,但与应用线程一起运行。
  • 关键技术:采用SATB(Snapshot-At-The-Beginning) 算法来解决对象消失问题(详见下文)。

d) 最终标记 (Final Marking)

  • 任务:处理在并发标记期间,由于用户程序运行而导致变动的少量SATB日志记录,并完成存活对象的标记。
  • 状态:需要 STW。此阶段会进行全局引用处理(如类卸载)和空Region的回收。

e) 清理阶段 (Cleanup)

  • 任务
  • 统计每个Region中完全空闲的Region和存活对象比例。
  • 根据用户期望的停顿时间,对Region进行排序,确定一个回收计划。
  • 更新记忆集(RSet),例如,处理那些指向即将被回收的Region的卡页。
  • 状态:此阶段部分STW(统计和排序),部分并发(RSet清理)。

3. 混合回收 (Mixed GC)

  • 触发条件:当老年代Region的堆占用比例达到阈值(-XX:InitiatingHeapOccupancyPercent,默认45%)时,在并发标记周期结束后,并不会立即开始Mixed GC,而是会开始一系列以Mixed GC为主的收集阶段。
  • 过程:Mixed GC会同时回收一部分年轻代的Region和一部分老年代的Region(这些老年代Region是在并发标记周期中被识别为垃圾比例最高的)。回收的过程与Young GC类似,也是进行对象拷贝(复制算法)。
  • 目标:尽可能地在用户指定的停顿时间内,回收尽可能多的垃圾区域。

4. Full GC

G1的设计目标是尽量避免Full GC。但如果对象分配速度过快,在Mixed GC来不及回收时,或者并发标记周期完成前老年代就被填满,就会导致并发失败(Evacuation Failure),从而触发一次完全STW的、使用单线程标记-整理算法的Full GC。这是G1的失败预案,性能极差,需要尽力避免。

下图展示了G1收集器从You

ng GC到并发标记,再到Mixed GC的完整工作流程与状态转换:

核心技术:RSet、卡表与SATB

记忆集(Remembered Set, RSet)

在分代收集中,年轻代GC需要扫描老年代来确认跨代引用,这是非常低效的。G1为每个Region都维护了一个记忆集(RSet)

  • 作用避免全堆扫描。RSet记录了其他Region中的对象指向本Region内对象的引用
  • 原理:这是一种“空间换时间”的策略。当进行年轻代GC时,只需要选定年轻代Region的RSet作为GC Roots的一部分,就可以找到所有来自老年代的引用,而无需扫描整个老年代。这极大地减少了GC的工作量。

卡表(Card Table)与RSet的实现

RSet在内部的实现通常是一种卡表(Card Table) 的扩展结构,可以理解为一种“反向的卡表”。

  • 传统卡表:记录“老年代→新生代”的引用。
  • G1的RSet:记录“其他Region→本Region”的引用,是一种“点对点”的更精细的结构。
  • 写屏障(Write Barrier):为了实现RSet,G1在引用字段赋值操作(如 objA.field = objB)前后插入了额外的代码(写屏障)。这个屏障会判断引用是否跨Region(例如,objB在Region A,而objB在Region B)。如果是跨Region引用,就会将对应引用信息记录到被引用Region(Region B)的RSet中。

SATB:解决并发标记的“对象消失”问题

与CMS使用增量更新(Incremental Update) 不同,G1在并发标记阶段使用 SATB(Snapshot-At-The-Beginning) 算法来保证标记的正确性。

  • 原理:SATB认为,在并发标记开始时(初始标记阶段),堆中所有存活的对象构成一个逻辑上的“快照”。在并发标记过程中,如果某个存活对象被删除了(即变为垃圾),G1会通过写屏障将这种变化记录下来。
  • 具体操作:在引用赋值前,写屏障会将被覆盖的旧引用值(即将被删除的引用)记录下来,放入一个专门的缓冲区。在最终标记阶段(STW),G1会将这些记录下的旧引用作为根,重新扫描一次。确保在快照中存活的对象,即使在并发阶段被删除了引用,也不会被错误回收
  • ** vs CMS:CMS关注的是新增的引用(增量更新),而G1关注的是被删除**的引用(SATB)。SATB的效率通常更高,因为在并发阶段,产生的删除记录通常比新增记录要少。

G1的优缺点与调优

优点

  1. 可预测的停顿:能通过 -XX:MaxGCPauseMillis 设定目标停顿时间。
  2. 高吞吐量与低延迟:兼顾了高吞吐量和低停顿。
  3. 无内存碎片:从整体上看是基于“标记-整理”算法(Region间),从局部(两个Region之间)看是基于“复制”算法,这意味着G1运作期间不会产生内存空间碎片

缺点与调优

  1. 内存占用与额外执行负载(Footprint & Overhead)
  2. 问题:RSet和卡表维护需要占用额外的内存(通常为堆大小的10%~20%)。写屏障也会给程序运行带来额外的负担。
  3. 调优:监控RSet的大小,通常无需手动干预。
  4. 并发失败(Evacuation Failure)与Full GC
  5. 问题:这是G1最需要避免的问题。通常由对象晋升过快巨型对象分配导致。
  6. 调优
  7. 增加堆大小更早地触发标记周期(降低 -XX:InitiatingHeapOccupancyPercent)。
  8. 增加并发标记的线程数-XX:ConcGCThreads)。
  9. 避免巨型对象:G1对大对象(大小超过Region一半的对象)有特殊处理,会将其放入专门的Humongous Region。频繁分配/回收大对象会严重影响性能。
  10. ** Mixed GC回收效率不足**:
  11. 问题:垃圾很多,但每次Mixed GC回收的Region却不多。
  12. 调优:降低 -XX:G1MixedGCLiveThresholdPercent(默认85%),让存活对象比例超过85%的Region也可以被回收。增加 -XX:G1MixedGCCountTarget,让一次Mixed GC周期内能进行更多次的混合收集。

总结

G1是一款革命性的收集器,它通过Region分区模型、可预测的停顿时间和高效的并发标记,成功地在延迟和吞吐量之间找到了一个优秀的平衡点。其核心机制——RSet实现了精准的跨代引用跟踪,SATB算法解决了并发标记的准确性难题——不仅是G1的基石,也深刻影响了后续ZGC等新一代收集器的设计。虽然其调优参数比CMS更为复杂,但理解和掌握G1,是迈向JVM性能调优专家之路的关键一步。

相关文章
|
26天前
|
监控 算法 Java
深入理解JVM《垃圾收集(GC)机制与算法 - 宇宙的清洁工》
Java通过垃圾收集(GC)实现自动内存管理,避免手动释放内存导致的泄漏或崩溃。主流JVM采用可达性分析算法判断对象生死,结合分代收集理论,使用标记-清除、复制、标记-整理等算法回收内存。G1、ZGC等现代收集器进一步提升性能与停顿控制。
|
26天前
|
存储 缓存 算法
深入理解JVM《JVM内存区域详解 - 世界的基石》
Java代码从编译到执行需经javac编译为.class字节码,再由JVM加载运行。JVM内存分为线程私有(程序计数器、虚拟机栈、本地方法栈)和线程共享(堆、方法区)区域,其中堆是GC主战场,方法区在JDK 8+演变为使用本地内存的元空间,直接内存则用于提升NIO性能,但可能引发OOM。
|
20天前
|
机器学习/深度学习 人工智能 自动驾驶
跳出循环:当AI不再是“模仿”,而是“思考”
跳出循环:当AI不再是“模仿”,而是“思考”
155 94
|
26天前
|
存储 监控 Oracle
深入理解JVM《ZGC:超低延迟的可扩展垃圾收集器》
ZGC是JDK 11引入、15正式发布的低延迟垃圾收集器,目标是堆大小无关的10ms内停顿。其核心通过“着色指针”和“读屏障”实现标记与重定位的并发执行,极大减少STW时间,适用于大内存、高实时场景,虽有CPU开销但吞吐影响小,调优简单,是未来Java GC的发展方向。
|
12天前
|
存储 人工智能 安全
揭秘 MCP Streamable HTTP 协议亲和性的技术内幕
函数计算推出MCP Streamable HTTP亲和机制,支持会话级请求绑定,解决传统Serverless对会话应用支持不足的问题。实现高效生命周期控制,并支持Bearer认证,助力开发者构建更稳定、安全、高性能的AI应用服务。
283 25
|
19天前
|
人工智能 自然语言处理 安全
用AI重构人机关系,OPPO智慧服务带来了更“懂你”的体验
OPPO在2025开发者大会上展现智慧服务新范式:通过大模型与意图识别技术,构建全场景入口矩阵,实现“服务找人”。打通负一屏、小布助手等系统级入口,让服务主动触达用户;为开发者提供统一意图标准、一站式平台与安全准则,降低适配成本,共建开放生态。
145 31
|
22天前
|
存储 人工智能 运维
别再靠脚本“救火”了!让智能数据治理接管你的运维世界
别再靠脚本“救火”了!让智能数据治理接管你的运维世界
140 14
|
29天前
|
文字识别 自然语言处理 数据处理
《大模型赋能文化遗产数字化:古籍修复与知识挖掘的技术实践》
本文记录大模型赋能文化遗产数字化的实践,针对古籍异体字识别难、残缺文本补全不准、隐性知识难挖掘、多模态数据割裂、中小机构部署难、知识难更新等痛点,提出对应方案:搭建古籍文字与语境知识库提升识别理解率,以多源史料关联与历史逻辑约束实现文本精准补全,构建多层级框架挖掘隐性知识,设计多模态语义对齐整合多元信息,通过轻量化优化与混合部署降低使用门槛,建立动态机制保障知识迭代。优化后多项关键指标显著提升,为古籍数字化提供有效路径。
113 9
|
29天前
|
人工智能 监控 安全
员工使用第三方AI办公的风险与解决方案:从三星案例看AI的数据防泄漏
生成式AI提升办公效率,也带来数据泄露风险。三星、迪士尼案例揭示敏感信息外泄隐患。AI-FOCUS团队建议构建“流式网关+DLP”防护体系,实现分级管控、全程审计,平衡安全与创新。
|
26天前
|
存储 算法 搜索推荐
《数据之美》:Java数据结构与算法精要
本系列深入探讨数据结构与算法的核心原理及Java实现,涵盖线性与非线性结构、常用算法分类、复杂度分析及集合框架应用,助你提升程序效率,掌握编程底层逻辑。