Lazy loaded image
🚮G1
Words 2657Read Time 7 min
2025-8-11
2025-9-12
type
status
date
slug
summary
tags
category
icon
password
原文

G1

  • 软实时、低延时、可设定目标
  • JDK9+默认的 GC
  • 适用于较大的堆(> 4~6G)
  • 用于替代 CMS
可设置最大的 STW 停顿时间(可设定目标)
—XX:MaxGCPauseMillis = N
default: 250ms
年轻代 GC 算法
  • STW、Parallel 、Copying
老年代 GC 算法
  • Mostly-concurrent marking(vs CMS)
  • Incremental compaction
最大特征将大空间分成若干小区域,能实现一些更复杂、更精细的功能。
将堆分成若干个等大的区域
  • -XX:G1HeapRegionSize=N (默认值2048)
每个Region 不再是固定的新生代、老年代这样
 

G1的内存布局

 

分区(Heap Region ,HR)

G1 的每一个分区都对应一个分区类型。
  • 自由分区(Free Heap Region,FHR)
  • 新生代分区(Young Heap Region,YHR)
    • Eden
    • Survivor
  • 大对象分区(Humongous Heap Region,HHR)
    • 大对象头分区
    • 大对象连续分区
  • 老生代分区(Old Heap Region,OHR)
 

自由分区问题

 
有的资料会说分区类型是新生代、老年代、大对象 三个分区类型。关于自由分区的时网上查询的资料有冲突。根据源码的描述,FreeTag =0 是不是对应着自由分区。
HeapRegionType源码

Region 大小和数量

Region 的大小只能为1MB、2MB、4MB、8MB、16MB和32M(jdk 版本不同可能不一样)。如果不是,则会向下取到最接近的值。例如设置 31 则会向下取整 16。
分区默认的大小最小值为1MB,最大值为 32MB。也就是说堆的最大内存为 32 * 2048 = 64GB。
大对象:大于等于分区的一半就会放到 H 区(大对象分区)
在不指定HR大小的时候,由G1启发式地推断HR大小。
-XX:G1HeapRegionSize=N 设置 Region 大小
在实际应用中,不建议设置该参数。设置固定大小,会使G1 的自动调节失效。
Region最大值计算: 2048为默认的分区个数
Region的数量 = 堆大小/Region_size

跨代/Region引用

notion image

G1的三种垃圾回收方式

新生代回收(YGC): 只回收新生代区域,代价低/频率高
混合回收(MixGC): 回收全部新生代+部分老年代,频率一般。
老年代占据堆空间超过 45% 会触发。这个命名只有G1 才有。
完全回收(FullGC): 全部堆空间,代价高/频率低,系统距离奔溃不远了
 
 
 

YGC 过程

什么时候触发

当内存分配的时候,剩余的空间不能满足要分配的对象时就会优先触发YGC。
 

YGC 过程

STW(Evacuation Pause)
  • 构建 CS(Eden+Survivor)
  • 扫描 GC Roots
  • Update RS: 排空 Dirty Card Queue
  • Process RS: 找到被哪些那些老年代对象引用
  • Object Copy
  • Reference Processing
G1记录每个阶段的时间,用于自动调优。
记录 Eden/Survivor的数量和 GC 时间
  • 根据暂停目标自动调整 Region 的数量
  • 暂停目标越短,Eden 数量越少
    • G1 可以设定目标时间,如果设置时间过短,它将处理的 Region 减少。
  • -XX:+PrintAdaptiveSizePolicy
    • 用 “自适应大小调整策略” 的日志输出
  • -XX:+PrintTenuringDistribution
    • 打印对象的 “存活年龄分布” 信息,主要针对年轻代的对象。

MixedGC

新生代+部分老年代一起回收,这就是混合回收。
正常情况下,新生代全部能回收,老年代会回收一部分
STW, Parallel, Copying

混合回收什么时候发生?

在 YGC 之后,已分配内存超过内存总容量的 45% 会触发。参数是-XX:InitiatingHeapOccupancyPercent
混合回收、Full GC 都是同时处理新生代和老年代。

对象什么时候进入老年代

Eden 区的对象满足以下条件会进入老年代
  1. 如果一些对象经过几轮 YGC 仍然存活,或者触发了动态年龄判断规则
  1. 或者存活的对象在 S 区放不下了,都会让对象进入老年代
  1. 大对象直接进入单独的大对象 Region,但是大对象区域本身就占用的是老年代区域
    1.  

哪些 Region 会被回收

关于 Collect Set:
MixedGC 包含了新生代所有分区和老年代部分分区
是否要放入 CSet: XX:G1MixedGCLiveThresholdPercent,默认为 85% 即 “存活对象占比 ≤ 15%” 的老年代 Region 会被优先选中(垃圾越多,回收价值越高)。
 

YGC与MixedGC 是什么关系

YGC 是 MixedGC的前奏,YGC 完成,就代表 mixedGC已经完成了初始化标记阶段,YGC已经帮 MixedGC已经帮 MixedGC干完了初始化的活。
MixedGC之前一定先进行一次YGC

MixedGC是否会真的执行

执行条件:XX:G1HeapWastePercent 默认5%。也就是说可回收的空间占总空间的比例大于5%才会启动 MixedGC。否则即使并发标记已经完成也不执行。
并发标记以后,发现回收的价值不高,就不执行。

MixedGC回收会多次执行

停顿时间:
根据其他条件计算出 CSet里有 400 个 Region满足回收的条件,但是根据停顿时间一次只能回收 50 个,怎么办?
分成多次,一次完成 50 个,8 次搞定
XX:G1MixedGCCountTarget 默认为 8即 MixedGC对 CSet 分配回收,最多分成 8次。

MixedGC过程

  1. 初始标记阶段(STW)
    1. 标记出所有由 GCRoot 等直接引用的对象,会暂停用户程序运行
  1. 并发标记阶段
    1. 标记上一步中标记的所有引用对象 ,执行时间略长。用户程序也会同时执行,不会 STW。
      在并发标记对象的引用关系可能存在变更。
  1. 再标记阶段
    1. 标记出上一个阶段没有被标记的对象,会 STW,执行速度非常快(影响小)
  1. 存活对象计数阶段
    1. 统计出每个 Region 存活对象的数量
  1. 垃圾回收阶段
    1. 选择回收价值较高的区域,把存活对象复制到新分区。
       

Full GC

什么时候触发 Full GC

YGC和 MixedGC都不够(无法分配对象)触发 Full GC
永久代满了也会触发 Full GC

Full GC是危险的

Full GC可能进行两次,第二次是回收软引用,如果对象仍然无法分配,系统基本要 OOM 了。

Full GC 如何复制对象

Full GC使用”标记-压缩“(因为没有 Region 可以使用复制了),时间更慢,代价更高。

Full GC的过程

  1. 标记活跃对象,然后计算对象的新地址
    1. Full GC时,进入标记阶段,标记所有的存活对象,这个过程与 YGC MixedGC基本类似。
  1. 更新引用对象的地址
    1. 逐个遍历每个 Region,每个 Region 从头开始,找到存活的对象指向接下来被回收的新位置。
      Region 里的对象之间可能也有引用关系,这里还要将 Region 内的对象的引用指向新的位置。
  1. 移动对象完成压缩
    1. 复制对象,将对象复制到新位置上,完成压缩。
  1. 后处理
    1. 尝试调整整个堆空间的大小
    2. 遍历堆,重构 RSet,Region 里的对象都发生了变化。
    3. 清除dirty Card队列,并把所有的分区都认为是old分区。
    4. 记录各种信息,

G1 停顿预测模型

G1 是一个响应时间优先的 GC 算法,用户可以设定整个 GC 过程的期望停顿时间,由参数 MaxGCpauseMillis 控制,默认值200MS。
这里的期望停顿时间不是硬性他条件,G1会努力在这个目标停顿时间内完成垃圾回收的工作,但是它不能保证,即也不可能完成(设置了太小的停顿时间,新生代太大等)
G1 的预测逻辑是基于衰减平均值和衰减标准差
衰减平均值: 给比较近的数据更高的权重,而不是简单的平均。

停顿时间太小可能产生的问题

CardTable & Remebered Set

GC 最早引入卡表的目的是为了对内存的引用关系做标记,从而根据引用关系快速遍历活跃对象。
CardTable
  • 表中的每个 entry 覆盖 512 Byte 的内存空间
  • 当对应的内存空间发生改变时,标记为 dirty
RemeberedSet
  • 指向 Card Table中的对应 entry
  • 可找到具体内存区域
空间换时间
  • 用额外的空间维护引用信息
  • 5%~10% memory overhead
    • 正个堆内存的 5%~10% 用于存储额外的空间维护引用信息
 
notion image
Remebered Set:用于 记录指向当前 Region 的外部引用,每个Region 都有一个专属的 RS,当 GC 收集某个 Region 时,只需扫描其 RS 即可获取所有外部引用,无需扫描整个堆。
 
上一篇
Spring AI Note
下一篇
线程池

Comments
Loading...