G1和ZGC垃圾收集器

G1垃圾收集器

引入问题G1 垃圾收集器架构,为什么可以指定时间。?

定义

G1 即 garbage first ,意思是优先处理那些垃圾多的内存块的意思。
G1 适用于新生代 和 老年代。是利用复制算法实现

G1是一款可以实现高吞吐量,同时可以保证GC停顿时间不会很长的垃圾收集器

G1的优势

  • 适用于大容量的内容,多核的处理器
  • 可以实现并发收集垃圾,和用户程序能同时运行
  • 整理内存不需要大量的暂停时间
  • 能保持较高吞吐量,可以预测GC停顿时间

G1架构

传统的垃圾收集器是将堆内存划分为几块固定大小的内存区域,eden , survivor , old 。
而G1 垃圾收集器是将内存划分了很多个小块Region,每一个小块都会被标记为eden 、 survivor 或者old 。
这样做的好处是,内存块的粒度更小,可以更加细化的管理内存,这样每一次垃圾回收就不需要把一大块的eden或者old内进行回收,只需要针对小块的region进行回收即可,减少停顿时间,这样还可以有效的避免内存碎片的问题,因为每次垃圾回收都是清空一整块的region区域,避免的内存碎片。这些小块的region可以高度并行化收集,有效的减少停顿时间。

G1 内存布局

G1的各代存储地址是不连续的,每一代都使用了n个不连续的大小相同的Region,每个Region占有一块连续的虚拟内存地址。如下图所示
在这里插入图片描述
在上图中,我们注意到还有一些Region标明了H,它代表Humongous,这表示这些Region存储的是巨大对象(humongous object,H-obj),即大小大于等于region一半的对象。H-obj有如下几个特征: * H-obj直接分配到了old gen,防止了反复拷贝移动。 * H-obj在global concurrent marking阶段的cleanup 和 full GC阶段回收。 * 在分配H-obj之前先检查是否超过 initiating heap occupancy percent和the marking threshold, 如果超过的话,就启动global concurrent marking,为的是提早回收,防止 evacuation failures 和 full GC。

为了减少连续H-objs分配对GC的影响,需要把大对象变为普通的对象,建议增大Region size。

一个Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围从1M到32M,且是2的指数。如果不设定,那么G1会根据Heap大小自动决定

G1可以让用户自己设置应用暂停时间,为什么可以做到这一点?

G1 回收大概有四步(全局并发标记):

  1. 初始标记:找出跟gc root 直接关联的对象,这里会短暂的暂停用户线程,停止应用
  2. 并发标记:根据可达性分析,找到其他存活的对象,这一步可以用户线程并发执行。
  3. 最终标记: 修正在并发标记期间因为用户程序而改变的数据,需要暂停用户线程。
  4. 筛选回收:对各个region的回收价值和成本进行排序,根据用户期望的GC 停顿时间制定回收计划。需要暂停用户线程。

因为筛选回收只回收一部分价值高的Region区的垃圾对象,时间是用户可控制的,而且停顿用户线程将大幅提高收集效率。回收时,采用“复制”算法,从一个或多个Region复制存活对象到堆上的另一个空的Region,并且在此过程中压缩和释放内存,避免内存碎片

所以能设置时间的主要原因就是在第四步里面,G1垃圾收集器不会回收整代内存,而是对每一块region进行排序,具体回收多少要看用户设置的停顿时间,优先回收那些垃圾最多的region区域

G1 缺点

对一些内存很小的应用,G1对内存进行部分回收根本不够用,所以始终都要进行整个堆的回收,那么G1反而不适合,因为G1算法更加复杂,可能比其他回收器还要差,因为G1适用于内存稍大一点的应用,一般来说至少4G以上。
在某些情况下,G1触发了Full GC,这时G1会退化使用Serial收集器来完成垃圾的清理工作,它仅仅使用单线程来完成GC工作,GC暂停时间将达到秒级别的。整个应用处于假死状态,不能处理任何请求

G1 GC模式

G1 Young GC

所有年轻代里的Region

Young GC主要是对Eden区进行GC,它在Eden空间耗尽时会被触发。在这种情况下,Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升到年老代空间。Survivor区的数据移动到新的Survivor区中,也有部分数据晋升到老年代空间中。最终Eden空间的数据为空,GC停止工作,应用线程继续执行。

注意全局并发标记(global concurrent marking)的那几个阶段,并不是young GC 里面必须的步骤,只是“全局并发标记”会利用young gc暂停的那个空间,顺便做一下标记。全局并发标记是为mixed gc服务的

global concurrent marking,它的执行过程类似CMS,但是不同的是,在G1 GC中,它主要是为Mixed GC提供标记服务的,并不是一次GC过程的一个必须环节。

Mixed GC

年轻代里的Region + 回收价值高的老年代Region

选定所有年轻代里的Region,外加根据global concurrent marking统计得出收集收益高的若干老年代Region。在用户指定的开销目标范围内尽可能选择收益高的老年代Region。 (global concurrent marking即全局并发标记)

  • Mixed GC时机
    Young GC发生的时机大家都知道,那什么时候发生Mixed GC呢?其实是由一些参数控制着的,另外也控制着哪些老年代Region会被选入CSet。 * G1HeapWastePercent:在global concurrent marking结束之后,我们可以知道old gen regions中有多少空间要被回收,在每次YGC之后和再次发生Mixed GC之前,会检查垃圾占比是否达到此参数,只有达到了,下次才会发生Mixed GC。 * G1MixedGCLiveThresholdPercent:old generation region中的存活对象的占比,只有在此参数之下,才会被选入CSet。 * G1MixedGCCountTarget:一次global concurrent marking之后,最多执行Mixed GC的次数。 * G1OldCSetRegionThresholdPercent:一次Mixed GC中能被选入CSet的最多old generation region数量。

除了以上的参数,G1 GC相关的其他主要的参数有:

  • 参数 含义
    -XX:G1HeapRegionSize=n 设置Region大小,并非最终值
    -XX:MaxGCPauseMillis 设置G1收集过程目标时间,默认值200ms,不是硬性条件
    -XX:G1NewSizePercent 新生代最小值,默认值5%
    -XX:G1MaxNewSizePercent 新生代最大值,默认值60%
    -XX:ParallelGCThreads STW期间,并行GC线程数
    -XX:ConcGCThreads=n 并发标记阶段,并行执行的线程数
    -XX:InitiatingHeapOccupancyPercent 设置触发标记周期的 Java 堆占用率阈值。默认值是45%。这里的java堆占比指的是non_young_capacity_bytes,包括old+humongous

避免使用以下参数:
避免使用 -Xmn 选项或 -XX:NewRatio 等其他相关选项显式设置年轻代大小。固定年轻代的大小会覆盖暂停时间目标,应该让jvm自己去适配大小

学习参考:
https://tech.meituan.com/2016/09/23/g1.html (美团资料)
https://www.cnblogs.com/aspirant/p/8663872.html(阿)
https://zhuanlan.zhihu.com/p/191253113
https://blog.csdn.net/qq_38294614/article/details/107746331

在这里插入图片描述

什么时候用G1

官网:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/G1.html#use_cases
(1)50%以上的堆被存活对象占用
(2)对象分配和晋升的速度变化非常大
(3)垃圾回收时间比较长

G1的瓶颈:

在介绍ZGC之前,首先回顾一下CMS和G1的GC过程以及停顿时间的瓶颈。CMS新生代的Young GC、G1和ZGC都基于标记-复制算法,但算法具体实现的不同就导致了巨大的性能差异。

标记-复制算法应用在CMS新生代(ParNew是CMS默认的新生代垃圾回收器)和G1垃圾回收器中。标记-复制算法可以分为三个阶段:

  • 标记阶段,即从GC Roots集合开始,标记活跃对象;
  • 转移阶段,即把活跃对象复制到新的内存地址上;
  • 重定位阶段,因为转移导致对象的地址发生了变化,在重定位阶段,所有指向对象旧地址的指针都要调整到对象新的地址上。
    下面以G1为例,通过G1中标记-复制算法过程(G1的Young GC和Mixed GC均采用该算法),分析G1停顿耗时的主要瓶颈。G1垃圾回收周期如下图所示:

学习参考美团博客:https://zhuanlan.zhihu.com/p/170572432
在这里插入图片描述

标记阶段停顿分析

  • 初始标记阶段:初始标记阶段是指从GC Roots出发标记全部直接子节点的过程,该阶段是STW的。由于GC Roots数量不多,通常该阶段耗时非常短。
  • 并发标记阶段:并发标记阶段是指从GC Roots开始对堆中对象进行可达性分析,找出存活对象。该阶段是并发的,即应用线程和GC线程可以同时活动。并发标记耗时相对长很多,但因为不是STW,所以我们不太关心该阶段耗时的长短。
  • 再标记阶段:重新标记那些在并发标记阶段发生变化的对象。该阶段是STW的。

清理阶段停顿分析

清理阶段清点出有存活对象的分区和没有存活对象的分区,该阶段不会清理垃圾对象,也不会执行存活对象的复制。该阶段是STW的。

复制阶段停顿分析

复制算法中的转移阶段需要分配新内存和复制对象的成员变量。转移阶段是STW的,其中内存分配通常耗时非常短,但对象成员变量的复制耗时有可能较长,这是因为复制耗时与存活对象数量与对象复杂度成正比。对象越复杂,复制耗时越长。

整体停顿分析

在四个过程中,
初始标记因为值标记GC root对象耗时较短。
并发标记可以不考虑,并发执行。
重新标记阶段,因为对象数少,耗时也短。
清理阶段因为内存分区数量少,耗时也短。(清点出有存活对象的分区和没有存活对象的分区,该阶段不会清理垃圾对象)
转移阶段因为要处理所有存活对象,包含复制对象属性,耗时会较长。因为G1的瓶颈主要是标记-复制中的转移阶段stw.

这时如果需要进一步减少停顿时间,就需要在复制阶段做文章

ZGC 垃圾收集器

JDK11新引入的ZGC收集器,不管是物理上还是逻辑上,ZGC中已经不存在新老年代的概念了,会分为一个个page,当进行GC操作时会对page进行压缩,因此没有碎片问题

ZGC 采用的是标记复制算法,可以低延迟的垃圾回收,停顿时间短。适合应用在更大内存和追求更低延迟场景
停顿时间短的原因主要是因为再对象复制转移阶段,采用的了对象的并发转移技术,对象的转移和应用线程可以同时进行。
对象转移过程中,地址会发生变化,如何保证应用线程访问的对象不会出错。

核心原理

ZGC垃圾回收过程几乎全部是并发,实际STW停顿时间极短,不到10ms。这得益于其采用的着色指针和读屏障技术。
主要采用了着色指针和读屏障技术
着色指针: 使用“空间换时间”思想,去降低GC停顿时间,这个空间是ZGC设置的虚拟空间
读屏障:将读出来的指针更新到对象的新地址上,保证访问的都是对象的新地址

知识点:
写屏障保证了在屏障之前的操作会强制更新到主内存,对其他线程是可见的,这种显示调用防止了指令重排序
读屏障可以让高速缓存中的数据失效,强制从主内存中加载数据,避免缓存不一样

核心收集流程

ZGC在回收周期大概分几步:原理参考链接https://zhuanlan.zhihu.com/p/170572432在这里插入图片描述

初始标记
并发标记
重新标记
并发转移准备
初始转移
并发转移

整个过程中的停顿只发生在初始标记,重新标记,初始转移阶段。
初始标记和初始转移分别都只需要扫描所有GC Roots,其处理时间和GC Roots的数量成正比,一般情况耗时非常短;
再标记阶段STW时间很短,重新标记的对象也不多,最多1ms,超过1ms则再次进入并发标记阶段。
即,ZGC几乎所有暂停都只依赖于GC Roots集合大小,停顿时间不会随着堆的大小或者活跃对象的大小而增加。
与ZGC对比,G1的转移阶段完全STW的,且停顿时间随存活对象的大小增加而增加,因为需要复制对象所有属性等会耗时很长。

ZGC 缺点

对吞吐量优先的场景,ZGC可能并不适合
因为,第一ZGC 是单代垃圾收集器,CMS这种是分代回收,单代回收的话每次处理的对象更多,更消耗cpu资源,垃圾收集时间长。第二,zgc采用的是读屏障技术,需要消耗额外的计算资源

  • 垃圾收集器跟内存大小的大致关系(不精确,需要根据实际环境测试)
    Serial:几十兆
    PS:上百兆 ~ 4G
    CMS:4G ~ 10G
    G1:10G~上百G
    ZGC: 4T - 16T(JDK13)
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EmineWang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值