哈喽大家好!
上一篇我们完整吃透了 JVM 基础GC算法与经典 ParNew+CMS 收集器体系,掌握了传统分代回收的底层逻辑与电商调优实战。
但在高并发、大内存、低延迟的现代互联网架构下,CMS 的内存碎片、STW抖动、降级FullGC 已经无法满足超大规模流量场景。因此 JDK 后续迭代推出了两大新一代主力收集器:G1 垃圾优先收集器、ZGC 超低延迟收集器。
一、G1垃圾收集器深度解析(Garbage-First)
G1 是 JDK9 默认垃圾收集器,是一款 面向服务器、面向大内存、可控低停顿、分区式、兼顾吞吐量与延迟 的折中型优秀收集器,完美替代 CMS,解决了内存碎片与停顿不可控问题。
1.1 G1收集器简介
G1(Garbage-First,垃圾优先)是一款基于分区、分代、并发、可预测停顿的垃圾收集器。
G1 彻底打破了传统堆内存「连续固定新生代、老年代」的物理划分,将整个堆内存切割为若干大小相等的独立分区(Region) ,JVM最多可以有2048个Region。每个 Region 动态扮演 Eden、Survivor、Old、大对象分区,GC 时优先回收垃圾最多、回收收益最高 的分区,因此得名「垃圾优先」。一般Region大小等于堆大小除以2048,比如堆大小为4096M,则Region大小为2M,当然也可以用参数"-XX:G1 HeapRegionSize "手动指定Region大小,但是推荐默认的计算方式。
G1保留了年轻代和老年代的概念 ,但不再是物理隔阂了 ,它们都是(可以不连续) Region的集合。
默认年轻代对堆内存的占比是5% ,如果堆大小为4096M ,那么年轻代占据200MB左右的内存 ,对应大概是100个Region ,可以通过" -XX:G1NewSizePercent"设置新生代初始占比 ,在系统运行中 ,JVM会不停的给年轻代增加更多的Region ,但是最多新生代的占比不会超过60% ,可以通过" -XX:G1MaxNewSizePercent"调整。年轻代中的Eden和Survivor对应的region也跟之前一样 ,默认8:1:1 ,假设年轻代现在有1000个region ,eden区对应800个 ,s0对应100个 ,s1对应100个。
一个Region可能之前是年轻代 ,如果Region进行了垃圾回收 ,之后可能又会变成老年代 ,也就是说Region的区域功能可能会动态变化。
G1垃圾收集器对于对象什么时候会转移到老年代跟之前讲过的原则一样 ,唯一不同的是对大对象的处理 ,G1有专门分配大对象的Region叫Humongous 区 ,而不是让大对象直接进入老年代的Region中。在G1中 ,大对象的判定规则就是一个大对象超过了一个Region大小的50% ,比如按照上面算的 ,每个Region是2M ,只要一个大对象超过了1M ,就会被放入Humongous中 ,而且一个大对象如果太大 ,可能会横跨多个Region来存放。
Humongous区专门存放短期巨型对象 ,不用直接进老年代 ,可以节约老年代的空间 ,避免因为老年代空间不够的GC开销。
Full GC的时候除了收集年轻代和老年代之外 ,也会将Humongous区一并回收。
核心定位:可控STW停顿、适合大内存、平衡延迟与吞吐量,是中小型互联网项目、中间件服务的首选收集器。
1.2 G1 GC完整执行步骤

G1 整体分为 初始标记、并发标记、最终标记、筛选回收 四大阶段,完整闭环流程如下:
-
初始标记 (initial mark ,STW):暂停所有的其他线程 ,并记录下gc roots直接能引用的对象 ,速度很快 ;
-
并发标记 (Concurrent Marking):同CMS的并发标记。
-
最终标记 (Remark ,STW):同CMS的重新标记。
-
筛选回收 (Cleanup ,STW):筛选回收阶段首先对各个Region的回收价 值和成本进行排序 ,根据用户所期 望的 GC 停顿时间(可以用 JVM 参数 - XX : MaxGCPauseMillis 指定)来制定回收计划 ,比如说老年代此时有1000个 Region都满了 ,但是因为根据预期停顿时间 ,本次垃圾回收可能只能停顿200毫秒 ,那么通过之前回收成本计算得知 ,可能回收其中800个Region刚好需要200ms ,那么就只会回收800个Region(Collection Set ,要回收的集合) ,尽量把GC导致的停顿时间控制在我们指定的范围内。这个阶段其实也可以做到与用户程序一起并发执行 ,但是因为只回收一部分Region ,时间是用户可控制的 ,而且停顿用户线程将大幅提高收集效率。
1.3 G1核心算法
-
整体基础:分代收集理论 + 分区回收思想
-
年轻代回收:标记-复制算法,保证新生代无碎片
-
老年代混合回收:标记-复制+整理复合算法,回收指定分区、整理内存、消灭碎片
-
并发标记底层:三色标记算法 + 写屏障 + 卡表记忆集,解决跨代引用与并发漏标
G1 摒弃了 CMS 单纯的清除算法,不管是年轻代或是老年代 ,回收算法主要用的是复制算法 ,将一个 region 中的存活对象复制到另一个 region 中 ,这种不会像 CMS 那样回收完因为有很多内存碎片还需要整理一次 ,G1采用复制算法回收几乎不会有太多内存碎片 。(注意 :CMS回收阶段是跟用户线程一起并发执行的 ,G1因为内部实现太复杂暂时没实现并发回收 ,不过到了Shenandoah就实现了并发收集 ,Shenandoah可以看成是G1的升级版本)
1.4 G1核心特点
-
分区式内存管理:堆划分为多个1~32MB Region,分区角色动态切换,灵活适配内存占用。
-
可预测停顿模型 :这是G1相对于CMS的另一个大优势 ,降低停顿时间是G1 和 CMS 共同的关注点 ,但G1 除了追求低停顿外 ,还能建立可预测的停顿时间模型 ,能让使用者明确指定在一个长度为M毫秒的时间片段(通过参数"- XX : MaxGCPauseMillis "指定)内完成垃圾收集。
-
垃圾优先回收:优先回收垃圾占比最高的分区,回收收益最大化。
-
无内存碎片 :与CMS的"标记--清理"算法不同 ,G1从整体来看是基于"标记整理 "算法实现的收集器;从局部上来看是基于"复制"算法实现的。
-
平衡吞吐与延迟:并发阶段提升吞吐量,STW可控保证低延迟。
-
自适应分代:动态调整新生代大小,适配不同业务流量峰值。
-
并行与并发 :G1能充分利用CPU、多核环境下的硬件优势 ,使用多个CPU(CPU或者CPU核心)来缩短Stop- The-World停顿时间。部分其他收集器原本需要停顿Java线程来执行GC动作 ,G1收集器仍然可以通过并发的方式让java程序继续执行。
毫无疑问 , 可以由用户指定期望的停顿时间是G1收集器很强大的一个功能 , 设置不同的期望停顿时间 , 可使得G1在不同应用场景中取得关注吞吐量和关注延迟之间的最佳平衡。 不过 , 这里设置的"期望值"必须是符合实际的 , 不能异想天开 , 毕竟G1是要冻结用户线程来复制对象的 , 这个停顿时间再怎么低也得有个限度。 它默认的停顿目标为两百毫秒 , 一般来说 , 回收阶段占到几十到一百甚至接近两百毫秒都很正常 , 但如果我们把停顿时间调得非常低 , 譬如设置为二十毫秒 , 很可能出现的结果就是由于停顿目标时间太短 , 导致每次选出来的回收集只占堆内存很小的一部分 , 收集器收集的速度逐渐跟不上分配器分配的速度 , 导致垃圾慢慢堆积。 很可能一开始收集器还能从空闲的堆内存中获得一些喘息的时间 , 但应用运行时间一长就不行了 , 最终占满堆引发Full GC反而降低性能 , 所以通常把期望停顿时间设置为一两百毫秒或者两三百毫秒会是比较合理的。
1.5 G1垃圾收集分类
1.5.1 年轻代GC(Young GC)
仅回收Eden与Survivor分区,触发频繁、停顿短、速度快,负责清理绝大多数短期朝生夕灭对象。YoungGC并不是说现有的Eden区放满了就会马上触发 ,G1会计算下现在Eden区回收大概要多久时间 ,如果回收时间远远小于参数 -XX:MaxGCPauseMills 设定的值 ,那么增加年轻代的region ,继续给新对象存放 ,不会马上做Young GC ,直到下一次Eden区放满 ,G1计算回收时间接近参数 -XX:MaxGCPauseMills 设定的值 ,那么就会触发Young GC
1.5.2 混合GC(Mixed GC)
不是FullGC ,老年代的堆占有率达到参数(- XX : InitiatingHeapOccupancyPercent )设定的值则触发 ,替代CMS FullGC。回收所有的Young和部分Old(根据期望的GC停顿时间确定old区垃圾收集的优先顺序)以及大对象区 ,正常情况G1的垃圾收集是先做MixedGC ,主要使用复制算法 ,需要把各个region中存活的对象拷贝到别的region里去 ,拷贝过程中如果发现没有足够 的空 region 能够承载拷贝对象就会触发一次Full GC
1.5.3 全局FullGC(降级兜底)
G1尽量避免FullGC,仅在内存极度不足、并发标记失败、分区分配失败时触发,停止系统程序 ,然后采用单线程进行标记、清理和压缩整理 ,好空闲出来一批Region来供下一次MixedGC使用 ,这个过程是非常耗时的。(Shenandoah优化成多线程收集了),生产需绝对避免。
1.6 G1常用生产参数设置
-XX:+UseG1GC:使用G1收集器
-XX:ParallelGCThreads:指定GC工作的线程数量
-XX:G1HeapRegionSize:指定分区大小(1MB~32MB ,且必须是2的N次幂) ,默认将整堆划分为2048个分区
-XX:MaxGCPauseMillis:目标暂停时间(默认200ms)
-XX:G1NewSizePercent:新生代内存初始空间(默认整堆5%)
-XX:G1MaxNewSizePercent:新生代内存最大空间
-XX:TargetSurvivorRatio:Survivor区的填充容量(默认50%) ,Survivor区域里的一批对象(年龄1+年龄2+年龄n的多个
年龄对象)总和超过了Survivor区域的50% ,此时就会把年龄n(含)以上的对象都放入老年代
-XX:MaxTenuringThreshold:最大年龄阈值(默认15)
-XX:InitiatingHeapOccupancyPercent:老年代占用空间达到整堆内存阈值(默认45%) ,则执行新生代和老年代的混合
收集(MixedGC) ,比如我们之前说的堆默认有2048个region ,如果有接近1000个region都是老年代的region ,则可能
就要触发MixedGC了
-XX:G1MixedGCLiveThresholdPercent(默认85%) region中的存活对象低于这个值时才会回收该region ,如果超过这
个值 ,存活对象过多 ,回收的的意义不大。
-XX:G1MixedGCCountTarget:在一次回收过程中指定做几次筛选回收(默认8次) ,在最后一个筛选回收阶段可以回收一
会 ,然后暂停回收 ,恢复系统运行 ,一会再开始回收 ,这样可以让系统不至于单次停顿时间过长。
-XX:G1HeapWastePercent(默认5%): gc过程中空出来的region是否充足阈值 ,在混合回收的时候 ,对Region回收都
是基于复制算法进行的 ,都是把要回收的Region里的存活对象放入其他Region ,然后这个Region中的垃圾对象全部清
理掉 ,这样的话在回收过程就会不断空出来新的Region ,一旦空闲出来的Region数量达到了堆内存的5% ,此时就会立
即停止混合回收 ,意味着本次混合回收就结束了。
1.7 G1收集器优化建议
-
合理设置停顿时间:不要过小(容易GC频繁)、不要过大(卡顿明显),业务系统建议100~150ms。
-
调大IHOP阈值:大内存机器可适当调高,减少并发标记频次,提升吞吐量。
-
适配Region大小:堆内存越大,Region适当调大,减少分区数量、降低管理开销。
-
避免大对象泛滥:G1大对象直接分配Humongous分区,长期堆积会触发GC频繁,需业务优化。
-
控制混合GC频率:通过调整老年代回收分区比例,避免连续多次Mixed GC造成抖动。
-
杜绝G1降级FullGC:预留足够堆空闲空间,避免内存打满触发兜底FullGC。
1.8 G1适用场景
-
堆内存较大(4G~16G)的服务端应用。
-
对GC停顿有一定要求,但不极致苛刻的业务系统。
-
需要平衡吞吐量与低延迟的中间件、后台服务、普通业务服务。
-
原有CMS系统频繁碎片、频繁FullGC的迁移替换场景。
-
50%以上的堆被存活对象占用
1.9 基于G1的JVM优化核心机制
-
动态自适应分代机制:根据流量波动、对象存活情况,实时调整新生代大小,避免空间浪费与GC频繁。
-
停顿预测模型:基于历史GC耗时、分区回收耗时,精准控制单次回收范围,保证停顿稳定可控。
-
增量式老年代回收:不一次性整堆回收,通过多次混合GC逐步清理老年代,避免长时间STW。
-
分区内存规整机制:每次回收均整理内存,彻底杜绝内存碎片堆积。
-
卡表+写屏障跨代优化:精准定位跨代引用,避免全堆扫描,大幅提升标记效率。
二、ZGC超低延迟垃圾收集器深度解析
ZGC 是 JDK11 推出、JDK17 稳定、JDK21 支持分代的超低延迟、可扩展、超大内存友好的新一代垃圾收集器,是目前互联网大厂高并发、低延迟、万亿级流量系统的终极GC方案。
2.1 ZGC收集器简介
ZGC(Z Garbage Collector)是一款几乎全程并发、亚毫秒级停顿、支持TB级大内存的现代化垃圾收集器。区别于G1的可控停顿,ZGC 将 STW 停顿压缩到极致,绝大多数GC阶段完全与用户线程并发执行,停顿时间与堆内存大小无关。
无论堆内存是8G、64G、128G,ZGC 停顿时间稳定控制在10ms以内,是目前低延迟性能天花板。
2.2 ZGC核心设计目标
-
超低延迟:STW停顿亚毫秒级,不随堆内存增大而变长。
-
超大内存可扩展:完美支持TB级堆内存,无性能衰减。
-
高吞吐量:并发执行占比极高,减少CPU资源抢占。
-
无内存碎片:支持内存整理,彻底解决碎片问题。
-
自适应硬件架构:支持NUMA多核架构,硬件级内存优化。
2.3 ZGC底层算法
ZGC 基于 并发三色标记 + 并发对象转移 + 指针着色技术 + 读屏障修正 实现,核心特点:
-
全程绝大部分阶段并发执行,仅短暂初始、结束标记停顿。
-
采用标记-转移-重映射机制,无内存碎片。
-
不依赖传统卡表记忆集,通过颜色指针+读屏障解决并发漏标与指针更新问题。
2.4 ZGC内存布局
ZGC 将堆内存划分为 Page分页模型,分为小页面、中页面、大页面,按需分配、动态回收,替代传统Region分区,内存利用率更高。
2.4.1 NUMA-aware 架构感知
NUMA(非统一内存访问)是现代多核服务器硬件架构,CPU访问本地内存速度远快于远端内存。
ZGC 原生支持 NUMA 感知分配:
-
小对象优先分配当前CPU本地内存,提升访问速度。
-
中大对象均衡分配,避免单节点内存失衡。
-
硬件级优化内存访问延迟,适配高并发服务器架构。
2.4.2 颜色指针(着色指针)核心原理
颜色指针是 ZGC 最核心、最标志性的底层技术,彻底颠覆传统GC标记方式。
64位系统中,对象指针有效寻址仅使用低46位,高位18位空闲。ZGC 利用空闲高位存储标记状态位,直接将GC标记信息记录在「对象指针」上,而非对象头或独立数据结构。
每个对象有一个64位指针 ,这64位被分为:
- 18位:预留给以后使用;
- 1位: Finalizable标识 ,此位与并发引用处理有关 ,它表示这个对象只能通过finalizer才能访问;
- 1位: Remapped标识 ,设置此位的值后 ,对象未指向relocation set中(relocation set表示需要GC的Region集合);
- 1位: Marked1标识;
- 1位: Marked0标识 ,和上面的Marked1都是标记对象用于辅助GC;
- 42位:对象的地址(所以它可以支持2^42 =4T内存):
2.4.2.1 四大颜色标记位
-
Mark0:第一轮标记周期标识
-
Mark1:第二轮标记周期标识
-
Remap:指针重定位标识
-
Relocate:对象转移标识
2.4.2.2 为什么需要两个Mark标记?
ZGC 采用交替双标记位机制 ,核心目的是区分新旧GC周期、避免标记重置开销。
传统GC每次回收需要清空所有标记状态,开销巨大。ZGC 通过 Mark0/Mark1 交替轮换,本轮用Mark0、下一轮用Mark1,无需批量重置标记,直接通过标记位区分对象归属当前/上一轮GC周期,极致提升并发效率。
2.4.2.3 颜色指针核心优势
-
零内存开销:无需维护卡表、记忆集、独立标记位图,节省大量堆外内存。
-
无漏标、无错标:指针自带状态,实时识别对象标记、转移状态。
-
并发能力拉满 :无需STW扫描修正,配合读屏障实时修复指针。大幅减少在垃圾收集过程中内存屏障的使用数量
-
超大内存友好:不随堆内存增大增加标记开销,扩展性极强。
-
**收集效率高:**一旦某个Region的存活对象被移走之后 ,这个Region立即就能够被释放和重用掉 ,而不必等待整个堆中所有指向该Region的引用都被修正后才能清理 ,这使得理论上只要还有一个空闲Region ,ZGC就能完成收集。
2.5 ZGC读屏障机制(核心修复能力)
ZGC 仅有读屏障、无写屏障,区别于CMS、G1的写屏障机制。
核心原理:用户线程每一次从堆中读取对象引用时,触发读屏障拦截,校验指针颜色状态:
-
若对象已转移、指针失效,读屏障实时修复指针地址,更新为新对象地址。
-
自动完成重映射,无需全局遍历更新引用。
-
保证业务线程永远访问有效对象,彻底解决并发转移指针错乱问题。
读屏障是 ZGC 能够全程并发转移、几乎无STW的核心保障。
2.6 ZGC完整运作流程
ZGC 一次GC周期分为四大核心阶段,仅两处短暂STW:
-
标记开始(短暂STW):暂停极短时间,扫描GC Roots,初始化标记状态。
-
并发标记:暂停极短时间,扫描GC Roots,初始化标记状态。与G1一样 ,并发标记是遍历对象图做可达性分析的阶段 ,它的初始标记(Mark Start)和最终标记(Mark End)也会出现短暂的停顿 ,与G1不同的是 , ZGC的标记是在指针上而不是在对象上进行的 , 标记阶段会更新染色指针中的Marked 0、 Marked 1标志位。
-
标记结束(短暂STW):最终确认存活对象,收尾标记修正。
-
并发预备转移:这个阶段需要根据特定的查询条件统计得出本次收集过程要清理哪些Region ,将这些Region组成重分配集(Relocation Set)。ZGC每次回收都会扫描所有的Region ,用范围更大的扫描成本换取省去G1中记忆集的维护成本。
-
并发对象转移:对象转移是ZGC执行过程中的核心阶段 ,这个过程要把重分配集中的存活对象复制到新的Region上 ,并为重分配集中的每个Region维护一个转发表(Forward Table) ,记录从旧对象到新对象的转向关系。ZGC收集器能仅从引用上就明确得知一个对象是否处于重分配集之中 ,如果用户线程此时并发访问了位于重分配集中的对象 ,这次访问将会被预置的内存屏障(读屏障)所截获 ,然后立即根据Region上的转发表记录将访问转发到新复制的对象上 ,并同时修正更新该引用的值 ,使其直接指向新对象 ,ZGC将这种行为称为指针的" 自愈"(Self-Healing)能力。
1 ZGC的颜色指针因为" 自愈"(Self‐Healing)能力,所以只有第一次访问旧对象会变慢, 一旦重分配集中某个Region的存活对象都复制完毕后, 2 这个Region就可以立即释放用于新对象的分配,但是转发表还得留着不能释放掉, 因为可能还有访问在使用这个转发表。 -
并发重映射:重映射所做的就是修正整个堆中指向重分配集中旧对象的所有引用 ,但是ZGC中对象引用存在" 自愈"功能 ,所以这个重映射操作并不是很迫切。ZGC很巧妙地把并发重映射阶段要做的工作 ,合并到了下一次垃圾收集循环中的并发标记阶段里去完成 ,反正它们都是要遍历所有对象的 ,这样合并就节省了一次遍历对象图的开销。一旦所有指针都被修正之后 , 原来记录新旧对象关系的转发表就可以释放掉了。
全程绝大部分时间业务线程无阻塞运行,用户无任何卡顿感知。
2.7 ZGC现存问题及解决方案
2.7.1 存在问题
-
小吞吐量损耗:读屏障有轻微CPU开销,高吞吐批量任务略弱于G1。
-
早期版本无分代:JDK21之前无分代机制,短期对象回收不够精细。
-
内存开销略高:分页管理存在少量内存元数据开销。
2.7.2 解决方案
-
升级JDK21+分代ZGC:引入分代回收,新生代快速回收,大幅提升吞吐量。
-
开启NUMA优化:硬件级降低内存访问延迟,抵消屏障开销。
-
针对性场景使用:低延迟核心业务用ZGC,批量后台任务用G1。
2.8 ZGC核心生产参数
# 开启ZGC收集器
-XX:+UseZGC
# 最大停顿时间
-XX:MaxGCPauseMillis=10
# 内存扩容阈值
-XX:ZGCAllocationThreshold=20
# 开启NUMA感知优化
-XX:+ZGC NUMA
# 开启分代ZGC(JDK21+)
-XX:+ZGenerational
# 打印GC日志
-Xloggc:zgc-gc.log
2.9 ZGC触发时机
-
内存占用阈值触发 :堆内存达到阈值自动触发并发GC。最多三次 ,在堆内存达到10%、20%、30%时触发 ,主要时统计GC时间 ,为其他GC机制使用。
-
分配失败触发:大对象、批量对象分配空间不足触发GC。
-
定时周期性触发 :空闲时段自动触发轻量GC,提前清理垃圾。默认为不使用 ,可通过ZCollectionInterval参数配置。
-
手动触发:System.gc()(生产建议禁用)。
三、垃圾收集器选型标准与底层补充机制
3.1 主流收集器完整选型方案
3.1.1 Serial / Serial Old
适用:客户端、低配置机器、本地测试、内存极小项目。 淘汰:高并发服务端禁止使用。
3.1.2 Parallel 吞吐量收集器
适用:后台批量任务、数据同步、离线计算、不关注延迟、追求吞吐量场景。 劣势:响应延迟不可控,不适合C端业务。
3.1.3 CMS
适用:老旧存量项目、中小流量业务。 劣势:碎片严重、易降级FullGC,新项目不推荐。
3.1.4 G1
适用:4~16G堆内存、需要平衡延迟与吞吐、普通线上业务、中间件服务、CMS迁移项目。 定位:通用型最优折中方案。
3.1.5 ZGC
适用:大内存(16G+)、秒杀、支付、订单、核心C端低延迟业务、万亿级流量系统。 定位:现代低延迟业务终极方案。
3.2 安全点与安全区域(GC停顿底层核心)
3.2.1 安全点(SafePoint)
GC 需要STW时,不会任意中断线程,必须等待所有用户线程进入安全点。
安全点 就是指代码中一些特定的位置,当线程运行到这些位置时它的状态是确定的,这样JVM就可以安全的进行一些操作,比如GC等 ,所以GC不是想什么时候做就立即触发的 ,是需要等待所有线程运行到安全点后才能触发。
这些特定的安全点位置主要有以下几种:
- 方法返回之前
- 调用某个方法之后
- 抛出异常的位置
- 循环的末尾
大体实现思想是当垃圾收集需要中断线程的时候 ,不直接对线程操作 ,仅仅简单地设置一个标志位 ,各个线程执行过程时会不停地主动去轮询这个标志 ,一旦发现中断标志为真时就自己在最近的安全点上主动中断挂起。轮询标志的地方和安全点是重合的。
核心作用:保证GC快照一致性,避免线程运行中引用错乱、对象状态异常。
常见问题:循环过长、无安全点,导致GC长时间无法进入STW,引发服务超时。
3.2.2 安全区域(SafeRegion)
针对休眠、阻塞、空闲线程设计。线程长时间停滞不执行代码,无法进入安全点,JVM 将该线程标记在安全区域内。
处于安全区域的线程,引用状态不会发生变化,GC可安全执行标记与回收,无需等待线程唤醒。
3.2.3 核心总结
安全点 :运行中线程暂停GC的位置保障。 安全区域:阻塞休眠线程的GC状态保障。 二者是所有GC STW停顿能够安全、准确执行的底层基石。
3.3 G1 与 ZGC 核心差异全方位对比
为方便快速记忆、面试复盘与生产选型,下表从底层架构、算法机制、性能表现、内存开销、适用场景等维度,系统汇总 G1 与 ZGC 的核心差异,覆盖所有高频考点。
| 对比维度 | G1 垃圾优先收集器 | ZGC 超低延迟收集器 |
|---|---|---|
| 发布版本与定位 | JDK9 默认收集器,平衡型通用收集器,兼顾吞吐量与延迟 | JDK11 推出、JDK17 稳定、JDK21 支持分代,主打极致低延迟、超大内存扩展性 |
| 内存管理模型 | 固定大小 Region 分区(1~32MB),动态分代,Eden/Survivor/Old 角色动态切换 | 动态 Page 分页模型(小/中/大页面),JDK21 前无分代,JDK21+ 支持分代ZGC,原生适配NUMA架构 |
| 核心底层机制 | 传统三色标记 + 写屏障 + 卡表/记忆集 实现跨代引用扫描 | 三色标记 + 颜色指针(指针着色) + 读屏障,无卡表、无记忆集 |
| STW 停顿特性 | 可控毫秒级停顿(100~200ms),停顿时间随堆内存增大略有上升 | 亚毫秒级极致停顿(稳定10ms内),停顿时间与堆内存大小无关,支持TB级堆 |
| 回收算法 | 新生代标记-复制,老年代混合标记-复制整理,增量式回收 | 全程并发标记-转移-重映射,无内存碎片,并发整理能力更强 |
| 并发修正方式 | 写屏障记录引用变动,重新标记阶段STW统一修正漏标 | 读屏障实时修复失效指针,无需全局扫描,全程并发修正 |
| 内存开销 | 需维护卡表、记忆集,存在固定堆外内存开销,大内存场景开销递增 | 依托指针高位存储标记,无卡表开销,元数据开销极低,超大内存扩展性极佳 |
| 吞吐量表现 | 整体吞吐量优秀,无额外CPU屏障损耗,适合批量高吞吐业务 | 读屏障存在轻微CPU开销,高吞吐离线任务略弱于G1,分代ZGC(JDK21+)大幅优化吞吐量 |
| 内存碎片处理 | 分区复制整理,基本无碎片,长期运行稳定性优于CMS | 并发对象转移+页面复用,彻底杜绝内存碎片,碎片处理能力优于G1 |
| 降级风险 | 内存紧张、分区分配失败时,触发单线程FullGC,卡顿严重 | 几乎无降级FullGC风险,并发回收能力极强,兜底机制更完善 |
| 最优堆内存规格 | 4G~16G 中小内存服务最优 | 16G+ 大内存、TB级超大内存服务最优 |
| 适用业务场景 | 普通业务服务、中间件、后台任务、CMS迁移项目,平衡延迟与吞吐 | 秒杀、支付、订单、核心C端高并发低延迟业务,超大内存分布式服务 |
选型极简结论 :追求高吞吐、均衡稳定 选G1;追求极致低延迟、大内存扩展选ZGC(优先JDK21+分代版本)。
四、全文终极总结
-
G1 基于分区模型、可控停顿、无内存碎片,是平衡型通用收集器,适配绝大多数线上业务。
-
ZGC 基于颜色指针+读屏障+NUMA优化,实现亚毫秒级停顿,是低延迟高性能终极方案。
-
双Mark标记位交替轮换,是ZGC规避标记重置开销、实现超高并发的核心设计。
-
G1依赖卡表写屏障,ZGC依赖颜色指针读屏障,底层实现完全不同。
-
业务选型:普通业务用G1、核心低延迟大流量业务用ZGC、批量任务用Parallel。
-
安全点与安全区域是GC安全停顿的底层保障,是排查GC卡顿、线程阻塞问题的关键知识点。