JVM垃圾收集器详解之Garbage First(G1)

G1简介

G1是一款非常具有特殊意义垃圾收集器的技术发展体现,因为相比G1之前的垃圾收集器,G1首次打破了基于老年代或者新生代一整块内存进行收集的设计思想,G1设计上依然有分代的思想,但是在内存上不在进行分代上的物理划分,也就是在一块大的内存区域中,既有年轻代也有老年代。

G1与CMS一样都是追求低停顿时间的垃圾收集器,但是由于G1在设计上的突破,使其能在更大的内存空间回收时,保持优秀的垃圾回收效率,这是G1之前的所有垃圾收集器所不能做到的。

G1中的分代设计

G1与其他的垃圾收集器相比不再有物理上的区域划分,而是直接使用一整块内存空间,并且划分为多个大小相等的独立区域(Region),每一个Region可以在逻辑上被划分为Eden区、Suvivor区、Old区、Humongous区,并且每一个类型的Region也没有固定的数量、大小与地址。

Humongous区是G1中新增的区域,专门用来存放大对象的,G1中定义一个对象如果超过Region大小的50%就属于大对象。

每个Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围为1MB~32MB,且应为2的N次幂。

在这里插入图片描述
每一个Region表示的含义是不固定的,Eden区可能会变成Old区,G1可以根据优化策略自行调整它们之间的比例,所以一般使用G1时,不建议手动配置新生代与老年代大小。

可预测的停顿时间

在G1中使用参数-XX:MaxGCPauseMillis,可以控制最大的停顿时间,这依然是一个软目标,但相比Parallel Scavenge设置而言,这要更加可控一些,因为现在的内存已经被划分为许多小的Region区,G1就可以根据每个Region区回收时大小和数量的一些经验值来进行选择性的回收,优先回收那些收益较高的Region,这一切都归功于Region的设计思想,当然如果你把这个值设置的太小,那么G1最终也只能牺牲每次回收的垃圾量而导致垃圾回收变得更频繁,这反而降低了总体性能。

G1中的回收步骤

初始标记
通过可达性分析,找到GCRoot根节点直接关联的对象,这个阶段依然是需要STW的,但是时间很短,而且是通过MinorGC时完成的,所以可以说这个阶段不需要额外的停顿时间。

并发标记
顺着GCRoots跟节点继续往下扫描,这个阶段耗时比较长,不过是与用户线程并发执行的。

最终标记
暂停用户线程,处理并发阶段时引用产生变化的一些对象。

筛选回收
依然需要暂停用户线程,统计Region中的数据,根据期望的停顿指标进行回收。

三色标记漏标问题

关于三色标记不理解的可以参考JVM垃圾回收算法—三色标记法分析。文章中讲述了CMS和G1下分别是如何处理漏标问题的。

RSet集合

之前在介绍CMS时提到了一个问题如何确定新生代对象是否存活?对于G1同样存在这个问题,就是那些在老年代的对象引用了新生代的对象,与CMS一样,G1也是把每一个Region划分为一些cardtable块,不同的是因为CMS的老年代只有一个,所以只需要维护一个对应的cardtable集合,而G1中的老年代会有很多个,这就需要维护很多个cardtable集合,所以G1在外面又加了一层集合,直接用来记录当前新生代被哪些老年代引用了,这个集合就是RSet,RSet可以理解为是一个Map集合,Key就是Region分区的起始地址,Value又是一个集合,集合中的元素就是这个Region分区中cardtable的脏下标。

如果cardtable不太了解,可以参考JVM垃圾收集器详解之CMS这篇文章,关于在CMS中是如何使用cardtable进行优化的,这与G1类似。

G1与CMS

G1并不是HotSpot中第一款并发垃圾收集器,在这之前已经有CMS了,CMS的回收步骤也与G1相似,并且也都是追求低停顿时间,不过由于CMS过于明显的缺点,注定是要被G1所取代的,JDK9中如果你配置使用CMS垃圾收集器,在项目启动时还会收到一条警告,提示CMS未来将会被抛弃。

G1如何解决CMS中垃圾碎片的问题

由于CMS中采用的是垃圾-清除算法,所以会产生内存碎片的问题,而在G1中回收的是Region区域,并且每个Region区域所代表的含义并不固定,所以G1在对多个Region进行回收的同时又能完成多个Region的整理,这从局部来看就是标记-复制的算法(一个Region中存活的对象复制到另一个Region中),从整理来看这又是标记-整理的算法,所以G1中不存在内存碎片的问题。

G1更适合大内存的服务器

G1相比之前的垃圾收集器的优势就在于并不会因为内存的变大而带来垃圾回收时间的增加,无论是CMS还是之前的垃圾收集器由于都是对整个内存空间进行回收,所以内存越大那么回收的对象就越多,同时回收的时间就越长,而G1不一样,G1在大内存上的优势会更加明显,因为G1并不会对整个内存进行回收,所以回收时间也不会随着内存增长而增长。

内存占用

由于G1中每个Region都需要维护一份RSet集合,而CMS只需要老年代中维护一份,这就导致G1中的RSet(和 其他内存消耗)可能会占整个堆容量的20%乃至更多的内存空间。

CPU负载消耗

CMS和G1都有因为并发标记过程用户线程改变对象引用关系的问题,二者都需要进行cardtable的维护,CMS和G1中都通过写后屏障进行维护,不过G1中为了实现原始快照的算法还需要写前屏障来跟踪指针的变化情况,所以在用户程序运行过程中会产生由跟踪引用变化带来的额外负担。

总结

无论如何G1都是一款里程碑式的垃圾收集器,并且在JDK9中也成为默认的垃圾收集器,如果你的服务器内存比较大(超过8G),那么建议你使用G1,不过如果你的服务器内存比较小,那么G1实际上就不太适用了,你可以选择CMS或者追求吞吐量的Parallel Scavenge和Parallel Old组合。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码拉松

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

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

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

打赏作者

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

抵扣说明:

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

余额充值