Java虚拟机:G1垃圾回收器
2026/7/28 18:35:13 网站建设 项目流程

一、 为什么需要 G1?(化整为零的思想)

在 G1 出现之前,无论是新生代(Minor GC)还是老年代(CMS),垃圾回收总是面对一整块“巨大的内存”。这导致:

  • STW 时间长:扫描全堆(或全代)非常耗时。

  • 内存碎片:CMS 使用“标记-清除”算法,回收后产生大量不连续空间,导致大对象无法分配,触发 Full GC。

G1 的核心突破区域化(Region)
G1 将整个堆内存(Heap)化整为零,划分成了大小相同的多个独立子区域(Region)。这就好比将一整块大的田地,划分成了一块块小方格,G1 可以针对性地回收某些“脏”的方格,而不必每次都犁遍整块地。

  • Region 的大小:JVM 启动时自动设定,通常为1MB ~ 32MB(且必须是 2 的幂)。默认会将整堆划分为2048个 Region。

  • 内存上限:通过参数-XX:G1HeapRegionSize=n可指定大小。由于最大只有 2048 个区域,所以最大支持32MB * 2048 = 64GB的堆内存。

✨ G1 的五大“杀手锏”特性:

  1. 并行性:回收期间多线程并行工作,充分利用多核 CPU 缩短 STW。

  2. 并发性:部分回收步骤与应用程序(用户线程)交替执行,不阻塞应用

  3. 分代 GC(逻辑上):虽然物理上不再区分新生代和老年代,但逻辑上依然存在 Eden、Survivor 和 Old 区,只是这些区域是可以动态切换的。

  4. 空间整理:通过复制算法回收,每次 GC 都会压缩空间,彻底解决内存碎片问题。

  5. 可预见性:由于分区,G1 可以预测停顿时间,只选择部分垃圾最多的 Region 进行回收(即CSet),极大降低了单次 GC 的影响。


二、 图解 G1 堆内存布局(Region 地图)

G1 的内存布局不再像传统堆那样是固定的EdenOld长条,而是由无数个Region方格组成:

  • 🟩 E (Eden):新生代,存活率低,GC 频繁。

  • 🟧 S (Survivor):幸存者区,用于存活对象在新生代之间的周转。

  • 🟦 O (Old):老年代,存放长生命周期对象。

  • 🟥 H (Humongous)巨型对象区(G1 独有的特殊区域)。当一个对象的大小超过单个 Region 的 50%,G1 会将其分配在连续的 H 区。

    • 坑点提示:如果 H 区装不下,需要连续拼接多个 H 区。如果找不到连续空间,就会导致 Full GC。

👉 核心逻辑:这些 Region 并不是固定不变的。在运行过程中,一个原本是Eden的区域可能经过回收后变为Old,完全取决于需求。这种“逻辑分代、物理分区”的设计,也是 G1 灵活性的来源。


三、 G1 的两大回收过程详解

G1 的回收分为两种核心场景:新生代 GC (Young GC)并发标记混合 GC (Mixed GC)

1. 新生代 GC (Young GC) —— “复制与晋升”

  • 触发时机:当 Eden 区的空间耗尽时,触发 Young GC。

  • 操作流程(基于复制算法):

    1. Eden ➡️ Survivor:将 Eden 区中存活的对象复制到一个 Survivor 区。

    2. Survivor ➡️ Survivor/Old:将原 Survivor 中的存活对象,根据年龄(Age)判定,移到另一个 Survivor 区或直接晋升到 Old 区(如果年龄达标或 Survivor 容量不足)。

    3. 清空 Eden:垃圾对象被彻底清理,形成连续的内存块。

  • 特点:这是一个完全 STW(Stop-The-World)的过程,但只扫描 Eden 区和相关的 Survivor 区,速度极快。

2. 并发标记周期与混合 GC (Mixed GC) —— “渐进式清理”

  • 目标:处理老年代(Old 区)和巨型区(H 区)的垃圾,降低整体停顿。

整个并发标记周期分为 6 个精准步骤(重点!):

步骤名称是否 STW核心动作详解
初始标记STW标记从GC Roots直接可达的对象。注意:这一步会伴随一次新生代 GC 执行,标记过程极短。
根区域扫描❌ 并发扫描 Survivor 区直接可达的老年代对象。(不能和新生代 GC 同时执行,因为新生代会修改 Survivor 区)
并发标记❌ 并发最长、最耗时的阶段。GC 线程与应用线程共同运行,扫描整个堆,查找存活对象。应用线程的变动会通过SATB (Snapshot-At-The-Beginning)算法记录快照,防止误判。
重新标记STW由于并发标记期间应用还在跑,结果可能不准确。这一步会补充修正标记,最终确定存活对象。
独占清理STW统计各个 Region 的存活对象比例,进行排序,并识别出垃圾最多(Garbage-First)的区域,确定下一次混合回收的CSet(集合)。
并发清理❌ 并发仅清除完全空闲的 Region。

👉 混合回收 (Mixed GC):在并发标记完成后,G1 会挑选出之前识别的“高收益” Region(即垃圾最多的那几个),执行一次混合回收。这个过程也是复制算法,会搬运存活对象到新的 Region,从而腾出大量空间并顺便完成内存碎片整理


四、 不得已的 Full GC —— 鱼和熊掌的平衡

尽管 G1 很强大,但世界并非完美。

  • Full GC 触发:当内存分配太快,并发收集跟不上的时候(例如并发标记还没结束,老年代就满了),G1 不得不降级,退化为单线程的 Full GC(即完全 STW,且速度很慢,性能骤降)。

  • 与 CMS 的相似性:和 CMS 类似,G1 依然无法 100% 避免全堆停顿,特别是在高并发、高频分配的大内存场景下。


五、 G1 实战调优参数(核心配置清单)

了解底层原理,是为了更好的调优。以下是 G1 最关键的几个参数:

参数名默认值作用及调优建议
-XX:+UseG1GC开启 G1 垃圾收集器。从 JDK 9 起,G1 已经是默认垃圾回收器。
-XX:MaxGCPauseMillis200ms停顿时间目标。G1 会为了达到这个目标,动态调整年轻代大小(Eden/Survivor)。注意:设置过小(如 50ms),会导致 GC 更频繁,吞吐量下降;设置过大,可能会使 Full GC 风险增加。
-XX:ParallelGCThreadsCPU 核心数设置并行回收时的GC 工作线程数。通常不建议修改,让 JVM 自动探测即可。
-XX:InitiatingHeapOccupancyPercent45触发并发标记的堆占用阈值。当整个堆占用率达到 45% 时,开始并发标记周期。
⚠️重要警醒:此参数设置过大(如 >50%),可能导致堆满而触发 Full GC;设置过小(如 <30%),会导致并发标记频繁启动,大量占用 CPU,拖累业务应用性能。

调优黄金法则:G1 的目标是“软实时”,即MaxGCPauseMillis是一个尽力而为的目标,而不是强制标准。在调优时,我们应该优先关注吞吐量Full GC 的频率,不要盲目追求极端的低停顿。


六、 附:G1 典型 GC 日志解析

看懂了日志,你才能知道 JVM 在底层干了什么。这是一段典型的 G1 Young GC 日志:

[GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0080719 secs] [Parallel Time: 5.1 ms, GC Workers: 10] <-- 并发工作线程数量,STW耗时 [Eden: 6144.0K(6144.0K)->0.0B(1024.0K) <-- Eden区 6MB -> 0MB,新Eden为1MB Survivors: 0.0B->0.0B <-- Survivor 空间变化 Heap: 8262.9K(10.0M)->6516.9K(10.0M)] <-- 整个堆从 8.2MB 降低到 6.5MB [Times: user=0.00 sys=0.00, real=0.01 secs]

日志解读

  1. GC pause (young) (initial-mark):这是一次 Young GC,并且伴随了“初始标记”。

  2. Heap: ... -> ...:说明堆内存发生了回收,垃圾被清理了。

  3. 时间开销real=0.01 secs(10ms),说明这次 GC 停顿几乎对用户无感知。

并发标记结束日志

[GC concurrent-root-region-scan-start] <-- 开始根区域扫描 (并发) [GC concurrent-root-region-scan-end, 0.0000227s] [GC concurrent-mark-start] <-- 开始并发标记 (并发) [GC concurrent-mark-end, 0.0008297s] [GC remark ... 0.0017108 secs] <-- 重新标记 (STW) [GC cleanup 7215K->7215K(10M), 0.0012619 secs] <-- 清理空闲区域 (并发)

总结

G1 作为一款现代化的垃圾回收器,其核心价值在于“化整为零”的 Region 设计“暂停时间可控”的混合回收机制。它不仅解决了 CMS 长期存在的内存碎片问题,还通过并发与并行极大地降低了 STW 的冲击。

但也要记住

  • 一切优化都有代价。为了降低停顿,G1 牺牲了部分内存空间(如额外的记忆集 Remembered Set)和 CPU 计算资源。

  • 在 64GB 以下的大内存、对响应时间(RT)敏感、多核 CPU 的服务器环境下,G1 是现阶段的最佳选择。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询