Java垃圾回收-G1垃圾收集器

本文介绍了Java G1垃圾收集器的革新之处,包括其整体结构、内存区域划分、可预测的停顿模型、空间分配机制,以及回收过程的四个关键步骤。G1以其预估停顿时间、面向堆内存的设计和混合回收特性,替代了CMS收集器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下文章来源于艾小仙 ,作者艾小仙

所有的垃圾回收器的目的都是朝着减少STW的目的而前行,G1(Garbage First)收集器的出现颠覆了之前版本CMS,Parallel等垃圾收集器的分代收集方式,从2004年sun发布第一篇关于G1的论文后,直到2012年JDK7发布更新版本,花了近10年的时间G1才达到商用程度,而到JDK9发布之后,G1成为了默认的垃圾收集器,CMS也变相的相当于被淘汰了。

G1结构

G1抛弃了之前的分代收集的方式,面向整个堆内存进行回收,把内存划分为多个大小相等的独立区域Region。
一共有4种Region:

  • 自由分区 Free Region
  • 年轻代分区 Young Region,年轻代还是会存在EdenSurvivor的区分
  • 老年代分区 Old Region
  • 大对象分区 Humongous Region

每一个Region的大小通过 -XX:G1HeapRegionSize来设置,大小为1~32MB,默认最多可以有2048个Region。

那么按照默认值计算G1能管理的最大内存就是32MB * 2048 = 64G

对于大对象的存储,存在Humongous概念,对G1来说,超过一个Region一半大小的对象都被认为大对象,将会被放入Humongous Region,而对于超过整个Region的大对象,则用几个连续的Humongous来存储

在这里插入图片描述

G1优势

垃圾收集器的最终目的都是为了减少STW造成的停顿,之前老的垃圾收集器CMS这种带来的停顿时间是不可预估的。

而G1最大的优势就在于可预估的停顿时间模型,可以通过参数-XX:MaxGCPauseMillis来设置允许的停顿时间(默认200ms),G1会收集每个Region的回收之后的空间大小回收需要的时间,根据评估得到的价值,在后台维护一个优先级列表,然后基于我们设置的停顿时间优先回收价值收益最大的Region

那么,这个可预测的停顿时间模型怎么计算和建立的?主要是基于衰减平均值的理论基础,衰减平均是一种数学方法,用来计算一个数列的平均值,给近期的数据更高的权重,强调近期数据对结果的影响,代码如下:

hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp
double get_new_prediction(TruncatedSeq* seq) {
  return MAX2(seq->davg() + sigma() * seq->dsd(),
              seq->davg() * confidence_factor(seq->num()));
}

davg:表示衰减值
sigma:表示一个系数,代表信贷度,默认值为0.5
dsd:表示衰减标准偏差
confidence_factor:表示可信度系数,用于当样本数据不足(小于5个)时去一个大于1的值,样本数据越少该值越大。

基于这个模型,G1希望根据用户设置的停顿时间(只是期望时间,尽量在这个范围内完成GC)来选择需要对哪些Region进行回收,能回收多大空间。

比如过去10次回收10G内存花费1s,如果预设的停顿时间是200ms,那么就最多可以回收2G的内存空间。

空间分配&扩展

既然G1还存在新生代和老年代的概念,那么新生代和老年代的空间是怎么划分的呢?

在G1中,新增了两个参数G1MaxNewSizePercentG1NewSizePercent用来控制新生代的大小,默认的情况下G1NewSizePercent值为5,也就是占整个堆空间的5%,G1MaxNewSizePercent 默认为60,也就是堆空间的60%。

假设现在我们的堆空间大小是4G,按照默认最大2048个Region计算,每个Region的大小就是2M。

初始新生代的大小就是200M,大约100个Region格子,动态扩展最大就是60% * 4G = 2.4G

在这里插入图片描述
实际上,初始新生代的空间大小逻辑还是挺复杂的。

首先,通过原有参数-Xms设置初始堆的大小,-Xmx设置最大堆的大小

  1. 通过原有参数-Xmn或新的参数G1NewSizePercentG1MaxNewSizePercent来设置年轻代的大小,如果设置了-Xmn相当于设置了G1NewSizePercent=G1MaxNewSizePercent
  2. 接着看是否设置了-XX:newRatio(表示年轻代与老年代比值,默认值为2,代表年轻代老年代大小为1:2),如果步骤1都设置了,那么忽略NewRatio,反之则代表G1NewSizePercent=G1MaxNewSizePercent,并且分配规则还是按照NewRatio的规则
  3. 如果只是设置了G1NewSizePercentG1NewSizeMaxPercent中的一个,那么就按照这两个参数的默认值5%和60%来设置
  4. 如果设置了-XX:SurvivorRatio,默认为8,那么Eden和Survivor还是按照这个比例来分配

如果新生代走默认的规则,每次动态扩展空间大小怎么办?

有个参数叫做-XX:GCTimeatio,表示GC时间与应用耗费时间比,默认为9,就是说GC时间和应用时间占比超过10%才进行扩展,扩展比例为20%,最小不小于1M

回收过程

G1的回收过程分为以下四个步骤:

  1. 初始标记,标记GC ROOT能关联到的对象,需要STW
  2. 并发标记,从GCROOTS的直接关联对象开始遍历整个对象图的过程,扫描完成后还会重新处理并发标记过程中产生变动的对象
  3. 最终标记,短暂暂停用户线程,再处理一次,需要STW
  4. 筛选回收,更新Region的统计数据,对每一个Region的回收价值和成本排序,根据用户设置的停顿时间制定回收计划。再把需要回收的Region中存活对象复制到空的Region,同时清理旧的Region。需要STW

G1其实也是类似的过程,总体可以分为这两种:

  1. 年轻代GC,年轻代Region在超过我们默认设置的最大大小之后就会触发GC,还是用的我们熟悉的复制算法,Eden和Survivor来回倒腾。
  2. Mixed GC 混合回收,混合回收类似于之前的Full GC概念,既会回收年轻代的Region,也会回收老年代的Region,还有我们新的Humongous大对象区域。触发规则根据参数-XX:InitiatingHeapOccupancyPercent(默认45%值),也就是说老年代Region达到整个堆内存的45%时触发Mixed GC。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值