深入浅出垃圾收集器-G1收集器

32 篇文章 4 订阅
22 篇文章 2 订阅

G1收集器

前面几篇博文中,我们聊了新生代收集器和老年代收集器(《深入浅出垃圾收集器-新生代收集器》《深入浅出垃圾收集器-老年代收集器》),这篇文章主要和大家介绍最后一款主流的垃圾收集器 G1收集器。

G1收集器的全称是:Garbge First
这是一款面向服务器端应用的垃圾收集器,它既可以用于新生代,也可以用于老年代。

内存布局

G1这款垃圾收集器,它带来了很多革命性的变化,首先是堆内存布局的变化。我们来看一下图:
在这里插入图片描述
和前面介绍的说有垃圾收集器都不一样,G1把整个java堆划分成了若干个大小相等的区域,每个区域叫做一个Region。
Region的大小可以通过参数-XX:G1HeapRegionSize指定。
它的取值范围是:1MB~32MB,并且为2的N次幂。

G1还对每个Region做了分类,分成了Eden Region、Survivor Region、Old Region、Humongous Region,前三个还比较好理解,依然是分代的概念,那么Humongous Region是用来干嘛的呢?
它是用来存储大对象的,某一个对象的大小超过了Region的一半,就会被认为是大对象,就会存储到Humongous Region里面去。

如果某一个对象超级大,甚至Humongous Region都存不下,那么会分布在多个连续的Humongous里面。

举个case:
假设,我们通过参数-XX:G1HeapRegionSize指定Region的大小为16M,那么某一个对象只要超过8M就会分配到Region里面去,然后G1会把这个Region标记为Humongous Region,那么如果某一个对象超级大,比如有30M,一个Region存不下,那么就会分配两个连续的Region存储这个大对象,并且把这两个对象都会标记为Humongous Region。那么事实上G1的Humongous也会被作为老年代的一部分去看待。

了解完G1的堆内存布局后,我们来探讨一下G1的设计思想。

设计思想

  • 内存分块(Region)
  • 跟踪每个Region里面的垃圾堆积的价值大小
  • 构建一个优先列表,根据允许的收集时间,优先回收价值高的Region

它首先把整个java堆划分成若干个大小的Region,然后跟踪每个Region里面的垃圾堆积的价值大小,比如说,我回收掉这个Region能够获取到多少的剩余空间,再然后,G1会在它的后台构建一个优先列表,你可以认为,它是对上述的价值大小做了一个排序,同时它会根据你允许的收集时间,去优先回收那些价值高的Region,这样就可以获得更高的垃圾回收效率。
那么说到这里,可以提一下,G1的全称叫做Garbge First,他的意思是这个垃圾收集器,会优先处理那些垃圾多的Region的意思。

经过分析,不难发现,G1的思想是化整为零,分而治之的,他会根据你设置的允许收集时间的阈值,一次只会去收集其中一部分Region的垃圾,这个其实本质上就是我们前几篇文章里提到的垃圾回收算法的增量算法的思想。
了解G1的设计思想后,我们再来聊一下G1的垃圾收集机制。

垃圾收集机制

G1提供了三种模式去收集垃圾,分别是:

  • Young GC
  • Mixed GC
  • Full GC

这三种GC的特性不一样,触发的条件也不一样。

我们先来看看Young GC是怎么玩的

Young GC

  • 当所有的Eden Region都满了的时候,就会触发Young GC
  • 所有的Eden Region里的对象,都会被转移到Survivor Region里面去
  • 原先Survivor Region中的对象会转移到新的Survivor Region中,或者晋升到Old Region(年龄达到阈值)
  • 空闲的Region 会被放入空闲列表中,等待下次被使用

可以发现,G1的Young GC和和前面聊的年轻代回收从过程上来说,基本上是一样的,只不过现在回收的单位是Region。

Mixed GC

Mixed GC是G1最能体现其设计思想的地方,也是它的巧妙之处,它的过程大概是这样的:

  • 老年代大小占整个堆的百分比达到一定阈值(可用-XX:InitiatingHeapOccupancyPercent指定,默认45%),就会触发
  • 一旦触发Mixed GC,会回收所有的Young Region,同时回收部分Old Region
Mixed GC执行过程

首先第一步是初始标记
Mixed GC的初始标记和CMS的初始标记大致是一样的,它也只是标记GC Roots能直接关联到的对象。

初始标记完成后,会进入到并发标记
这里的并发标记和CMS的并发标记也是类似的,这个过程耗时可能会稍微长一些,但是它会和用户线程并发执行,所以没有Stop The World

再之后就会进入到最终标记
最终标记也是为了修正在并发标记期间引起的变动,这个过程也存在Stop The World

最终标记完成后,就会进入到筛选回收的阶段
在筛选回收阶段,首先会对各个Region的回收价值和成本进行排序。
然后根据用户所期望的停顿时间(MaxGCPauseMillis)来制定回收计划并选择一些Region回收。
回收的过程大致是这样的:

  • 选择一系列Region构成一个回收集
    你可以这样理解,构造了一个Set然后把需要的Region放到这个Set里面去。
  • 把决定回收的Region中的存活的对象复制到空的Region中。
  • 删除掉需要回收的Region (G1采用的是复制算法,所以不存在内存碎片)
  • 存在Stop The World

通过这个过程我们可以发现,G1的Mixed GC除了并发标记阶段以外,其他的都是需要Stop The World的。
但是,由于它一次只会回收一部分的Region,所以这个Stop The World的时间是可控的。

Full GC

我们再来看一下Full GC是怎么玩的。
当G1 复制对象的时候内存不够,或者无法分配足够的内存(比如巨型对象没有足够的连续分区分配)时,会触发Full GC。
一旦触发Full GC,使用的是单线程的Serial Old模式
所以一旦触发Full GC就会长时间的Stop The World

因此 G1优化的一个重要原则就是:尽可能的减少Full GC的发生

那么怎样防止Full GC呢?

主要有三种思路:

  • 增加预留的内存(增大-XX:G1ReservePercent,默认为堆的10%)
  • 让G1更早的回收垃圾(减少-XX:InitiatingHeapOccupancyPercent,老年代达到该值就触发Mixed GC,默认45%)
  • 增加并发阶段使用的线程数(增大-XX:ConcGCThreads),但是这个参数会对应用的吞吐量有影响。

特点

我们最终来总结一下G1这款收集器的特点。

  • 可以作用在整个堆
  • 停顿时间是可控的
    可以使用MaxGCPauseMillis=n指定停顿时间
  • 无内存碎片

适用场景

  • 占用内存比较大的应用(一般来说,6G以上就可以考虑使用G1这款收集器了)
  • 适合替换CMS垃圾收集器(这也是官方设计G1的初衷)

总结

  1. G1它把内存划分成了若干个区域(Region)
  2. 包括Young GC、Mixed GC、Full GC
  3. Mixed GC步骤和CMS是有类似之处,但也有很多差异,CMS使用的是标记清除算法,所以会存在内存碎片的问题,而G1主要使用的是复制算法,把对象从一个Region复制到另外一个Region再把这个Region删除掉,所以没有内存碎片的问题。

如果实际项目中,你采用G1或者CMS都可以实现目标,那么我们该怎样去选择呢?

可以分为两种场景:
对于JDK 8 :都可以用
如果内存<=6G,建议CMS,如果内存>6G,考虑使用G1

那么如果你的项目使用的版本>JDK8 ,那么使用G1
因为CMS 从JDK9已经被废弃了。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值