HelloCoder HelloCoder
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
  • 《可观测和监控》
  • 《k8s学习心得》
随笔
关于作者
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
  • 《可观测和监控》
  • 《k8s学习心得》
随笔
关于作者
  • 《PureJavaCoderRoad》

    • 导读

    • Java基础

    • Java进阶

    • Java高阶

      • JVM

        • G1、ZGC有了解过吗?
        • JVM掌握的知识点
        • Java内存区域
        • Java内存模型
        • Java内存模型的happen-before
        • 类加载机制
        • 聊聊你熟悉的垃圾回收器
        • 谈谈Java的垃圾回收
        • 谈谈创建对象的过程
      • Java线程

      • 数据结构与算法

    • 开发辅助工具

    • 计算机网络

    • 数据库

    • JavaEE

    • 中间件

    • 架构

    • 建议

  • PureJavaCoderRoad
  • Java高阶
  • JVM
#G #ZGC #有了解过
HaC
2026-06-29
目录

G1、ZGC有了解过吗?

两者的发布时间线大致如下:

  • G1 (Garbage-First)
    • 初体验:随 JDK 6 Update 14 作为体验版发布。
    • 正式推出:在 JDK 7 Update 4 中被正式推出,标志着它成为一个可用的生产环境收集器。
    • 成为默认:自 JDK 9 起,G1 被设置为默认的垃圾回收器,取代了之前的 Parallel GC。
  • ZGC (Z Garbage Collector)
    • 实验性质:在 JDK 11 中作为实验性功能首次引入。
    • 生产就绪:在 JDK 15 中被正式宣布为生产就绪,可以放心在生产环境使用。
    • 重要演进:在 JDK 21 中进行了重大升级,引入了分代ZGC (Generational ZGC),进一步优化了性能和内存开销。

核心特性整理:

特性 G1 (Garbage-First) ZGC (Z Garbage Collector)
设计目标 在可控的停顿时间下,获得尽可能高的吞吐量。 实现极致低延迟,将GC停顿时间控制在亚毫秒级,且与堆大小无关。
核心技术 Region化分代:将堆划分为多个大小相等的Region,动态扮演Eden、Survivor、Old等角色。 SATB写屏障:用于并发标记,减少最终标记阶段的停顿。 着色指针 (Colored Pointers):在指针中编码GC状态信息。 读屏障 (Load Barriers):在应用线程读取对象引用时执行,实现“指针自愈”。
吞吐量影响 相对较低,对应用吞吐量影响较小,适合大多数通用场景。 会带来额外的CPU开销,官方表示吞吐量下降通常不超过15%。
适用场景 堆内存较大(4GB~64GB),追求平衡吞吐量和延迟的通用企业级应用,是默认推荐选项。 堆内存极大(可达16TB),对响应时间有极高要求的场景,如金融交易、实时风控等。

# G1核心设计是怎么样的?

G1核心设计是基于 Region 的堆内存布局。

G1 最根本的变革,是放弃了传统 GC(如 Parallel GC)中新生代、老年代物理连续的划分方式,而是将整个堆内存划分为大小相等的 Region(区域)。

  • 每个 Region 的大小通常在 1MB 到 32MB 之间,具体由 JVM 在启动时根据堆大小自动计算。
  • 每个 Region 在逻辑上可以扮演 Eden、Survivor、老年代(Old Gen) 甚至 大对象区(Humongous) 的角色。这个角色是动态的,在一次 GC 后,一个 Region 可以从 Eden 变为 Survivor,或者从 Old 变为空闲区(Free)。

这种设计的最大优势是灵活性。G1 不需要一次回收整个新生代或老年代,而是可以选择任意多个 Region 构成一个回收集(Collection Set, CSet),回收其中的垃圾。这正是“Garbage-First”(垃圾优先)名字的由来——它总是优先回收垃圾占比最高的 Region。

# G1 的优缺点

优点 缺点
可预测的停顿时间:通过停顿预测模型,G1 能将 GC 停顿控制在用户指定的范围内。 内存开销略高:由于需要维护 Region 元数据、RSet(Remembered Set)等,G1 的内存占用比 Parallel GC 稍高。
高吞吐量:在大多数场景下,G1 的吞吐量表现优异。 配置较复杂:虽然默认参数已经适用大多数场景,但要调优到最佳状态,需要深入理解其机制。
自动调优:G1 会根据运行时动态调整新生代大小、晋升阈值等参数,减轻了人工调优负担。 大对象分配敏感:频繁分配大对象可能导致 Full GC,需要额外关注。
适合大堆:在 4GB~64GB 的堆内存上,G1 表现尤为出色。

常用 JVM 参数:

参数 说明 默认值
-XX:+UseG1GC 启用 G1 GC(JDK 9+ 默认开启)
-XX:MaxGCPauseMillis 设定期望的最大停顿时间,单位毫秒。G1 会尽力达到这个目标,但不是硬性保证。 200ms
-XX:G1HeapRegionSize 设置每个 Region 的大小(必须为 2 的幂次,范围 1MB~32MB)。 由 JVM 根据堆大小自动计算
-XX:InitiatingHeapOccupancyPercent 触发并发标记周期的堆占用百分比(相对于整个堆)。 45%
-XX:G1ReservePercent 预留空闲 Region 的百分比,用于“晋升”失败时的兜底。 10%
-XX:ConcGCThreads 并发标记阶段的线程数。 根据 CPU 核心数自动计算
-XX:ParallelGCThreads STW 阶段并行执行的线程数。 根据 CPU 核心数自动计算

# 为什么ZGC的 STW 这么快?

ZGC的STW(Stop-The-World)之所以能达到毫秒乃至微秒级别,关键在于它从设计哲学到核心算法都进行了颠覆式的创新。它将几乎所有耗时的垃圾回收工作都变为与应用线程并发执行,而STW的暂停时间与堆的大小、存活对象的数量几乎没有关系。

它之所以这么快,主要归功于两项革命性的技术:

  • 着色指针 (Colored Pointers):ZGC颠覆了传统,不在对象头里记录GC信息,而是巧妙地利用64位指针中未被使用的位来存储标记和重定位状态。这让ZGC能在不访问内存的情况下快速判断对象状态,实现了“指针自愈”。
  • 读屏障 (Load Barriers):JVM在应用程序每次从堆中读取对象引用时,都会插入一小段“读屏障”代码。当应用线程通过“着色指针”访问对象时,读屏障会检查指针颜色。如果发现对象正被移动(颜色“坏了”),它会立即“治愈”这个引用,将其指向正确的新地址,确保应用线程永远访问到有效数据。

基于这两项技术,ZGC的整个GC周期(并发标记、并发转移等)几乎全程与应用并发执行,仅有初始标记、最终标记、初始转移这几个极短的阶段需要STW,并且这些阶段的耗时仅与GC Roots的数量相关,和堆的大小无关。

# 那么,既然ZGC这么好,是所有场景的首选吗?

答案是否定的。 任何技术选择都有其适用边界和代价,ZGC也不例外。

ZGC的核心优势是极致的低延迟,但它并非没有短板:

  1. 额外的CPU开销:因为读屏障在每次对象引用加载时都会执行,这会消耗一定的CPU资源。虽然官方测试表明整体吞吐量下降通常不超过15%,但在CPU资源本身就非常紧张的系统里,这个开销需要评估。
  2. 需要更大的堆内存(在某些情况下):在某些高分配速率的场景下,为了达到其设计的吞吐量目标,ZGC可能需要比G1更大的堆内存作为“喘息空间”。
  3. 内存占用显示异常:由于ZGC的多重映射技术,一些监控工具(如top)可能会误报其内存占用为实际值的数倍,给运维带来困扰。

因此,选择ZGC的核心原则是:是否愿意牺牲一点CPU利用率和可能的内存成本,来换取确定性极强的超低GC停顿。

# 如何选择垃圾回收器?

G1 是一个平衡型的垃圾回收器:

  • 它通过 Region 化内存布局 和 停顿预测模型,实现了可预测的低延迟。
  • 它通过 并发标记 和 增量回收,将大堆上的 GC 停顿控制在可接受范围内。
  • 它通过 自动调优,减轻了开发者的配置负担。

如果你的应用堆内存较大(>4GB),且对延迟有一定要求(不是极致的毫秒级,而是百毫秒级),G1 是一个非常稳妥的默认选择。如果你的应用对延迟有极致的亚毫秒级要求,或者堆内存超过 64GB,那么可以进一步考虑 ZGC。

ZGC STW 时间很短,但:

  • ZGC是“低延迟”场景下的利器,但它不是“银弹”。选择它需要你明确知道自己的应用是否能接受那15%以内的吞吐量开销,以及是否做好了适配和观察的准备。
  • 如果你的应用对响应时间非常敏感,常常因为GC停顿而影响用户体验或产生超时,那么ZGC是你的首选。在JDK 17及更高版本,通过 -XX:+UseZGC 参数即可启用,而**从JDK 21开始,Generational ZGC(分代ZGC)**进一步优化了性能和内存开销,是更具吸引力的选择。
  • 如果应用对延迟不敏感,更看重CPU资源的充分利用,那么G1甚至Parallel GC可能是更经济的选择。

对比:

回收器 核心目标 典型STW停顿 适用场景
ZGC 极致低延迟 < 1ms,且与堆大小无关 对响应时间有极高要求:金融交易、实时风控、在线游戏、大型互联网服务
G1 平衡吞吐量与延迟 通常几十到几百毫秒 通用场景:大多数微服务、Web应用,是GC领域的“万金油”
Parallel GC 最大化吞吐量 暂停时间可能较长(秒级) 批处理、后台计算:对响应时间不敏感,更关注任务处理速度的任务
#G#ZGC#有了解过
上次更新: 2026-06-29 17:10:35
最近更新
01
脏读、幻读
06-29
02
常见的存储系统
06-29
03
SPI机制
06-28
更多文章>
Theme by Vdoing | Copyright © 2020-2026 HaC
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式