JVM系列(十二) 垃圾收集器之 G1 Garbage First 收集器原理及过程

158 阅读8分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 30 天,点击查看活动详情

上几篇文章我们讲解了单线程垃圾收集器 Serial/SerialOld ,多线程垃圾收集器 Parallel Scavenge/Old, CMS标记清除垃圾收集器, 今天我们讲一下第四种通用垃圾收集器 G1垃圾收集器

垃圾收集器

  • 新生代收集器: Serial、ParNew、Parallel Scavenge;
  • 老年代收集器: Serial Old、CMS、Parallel Old;
  • 通用收集器:  G1;

收集器常用组合:

  1. Serial + Serial Old JVM设置-XX:+UseSerialGC
  2. Parallel Scavenge + Parallel Old JVM设置-XX:+UseParallelGC -XX:-UseParallelOldGC
  3. ParNew + CMS配合 JVM设置-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
  4. G1(不需要组合其他收集器) JVM设置-XX:+UseG1GC

收集器特点:

  • Serial + Serial Old 单线程
    • 单线程收集器,堆内存最小
  • Parallel Scavenge + Parallel Old 吞吐量优先
    • 吞吐量优先, 多线程收集
    • 堆内存较大,多核cpu
    • 新生代是eden/from/to 标记复制算法
    • 老年代是标记+整理算法 减少碎片化
  • Concurrent Mark Sweep 收集器 响应时间优先
    • 配合PerNew 实际上就是Serial的多线程版本,该收集器采用复制算法回收内存,期间会STW
    • 老年代CMS标记清除算法
  • G1 Garbase-First 垃圾处理优先
    • 从名字可知垃圾处理优先,哪里垃圾多,先处理哪里
    • 基于 “标记-复制” 算法,收集后不会产生内存碎片
    • 精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收

1. Garbage First收集器

什么样的场景适合我们采用G1垃圾收集器?

  • 配备多核cpu及大容量内存的机器,适用内存6-8G以上的服务器
  • 精准控制GC停顿时间,停顿时间是500ms以内
  • 服务器需要满足高吞吐量的的性能
  • 堆内存对象50%以上存活,对象分配和晋升的速度变化比较大
  • 垃圾回收时间长,影响到系统响应时间

G1(Grabage-First)是一款面向服务端应用的垃圾收集器,JDK 9以后的默认垃圾回收器, 他是一个并行回收器,它把堆内存分割为很多不相关的区域(Region)物理上是不连续的,堆空间被分为若干个区域(Region),这些区域中包含了逻辑上的年轻代和老年代, G1堆内存的回收是以Region为基本单位的

上面我们说过G1垃圾收集器化整为零,把对内存分为大小相等的多个区域Region,对每一个Region都可以当作 新生代Eden区,Survivor区,老年代Old空间,收集器对于不同的区域Region采用不同的策略去处理,从而达到最好的收集效果

为什么说Region既可以是Eden,又可以是Survivor,又可以是老年代Old空间?因为这些区域都是逻辑上的概念区域,并不是真实的区域,之前的垃圾收集器会对堆物理连续分区,新生代/老年代,但是G1只会在逻辑上区分,他不要求整个Eden区,年轻代,老年代都是连续的,也不要求新生代和老年代都维持固定的大小,每个Region区可能既包含老年代,又包含年轻代,又包含大对象存储的Humongous区域

参数-XX:G1HeapRegionSize参数,可以设定Region的大小,一般来说只要超过Region容量一半的对象就可以判断为大对象,大对象一般都会存入Humongous 区域当作老年代来看待

image.png

2.G1垃圾回收过程

G1在进行垃圾收集的时候,会根据每个Region预计垃圾收集所需时间预计回收内存大小的占比来选择对哪些Region区域进行回收

这种回收方式就不再有纯粹的Minor GC/Yong GCMajor GC/Full GC去回收某一块区域的分代对象,G1提供了三种垃圾回收方式,在不同的条件下被触发

  • YoungGC 年轻代回收
  • Mixed GC 混合回收的GC方式
  • Full GC 整堆回收
2.1 YoungGC 年轻代回收

G1与之前垃圾收集器的Young GC有所不同,并不是当新生代的Eden区放满了就进行垃圾回收,G1会预估Eden区回收大概需要多久的时间,预估本次gc大概会回收多少内存来选择是否回收

  • 预计回收时间远小于参数-XX:MaxGCPauseMills设定的值,那么G1就会增加年轻代的Region(可以从老年代或Humongous区划分Region给新生代),继续给新对象存放
  • 直到下一次Eden区放满
  • 如果G1计算回收时间接近参数-XX:MaxGCPauseMills设定的值,那么就会触发Young GC,开始进行垃圾回收

2.2 MixedGC 混合回收方式

G1的MixedGC 发生在老年代的空间达到一定的条件才触发

  • 参数-XX:InitiatingHeapOccupancyPercent的值会定义老年代的堆空间内存占比,如果达到了这个值就会触发Mixed GC
  • MixedGC 回收所有的新生代和部分老年代(根据用户设置的GC停顿时间来确定老年代垃圾收集的先后顺序)
  • MixedGC 也会回收Humongous区,Humongous会存放大对象,大对象也是当作老年代来处理
  • Mixed GC采用复制算法,需要把各个Region中存活的对象复制到另一个空闲的Region,如果在复制过程中发现没有足够的空Region放复制的对象,那么就会触发一次Full GC

2.3 FullGC 整堆回收

刚才我们说到 MixedGC 采用复制算法,如果你的空闲Region 不足以存放本次MixedGC 回收后还剩余的对象, 就会触发FullGC

  • FullGC停止系统程序,然后采用单线程进行标记、清理和压缩整理算法回收垃圾
  • 清理出部分Region区域来供下一次Mixed GC使用
  • FullGC 非常耗时,对各个Region区域回收时,会涉及大量的跨区域引用的对象
  • 跨区对象通过通过记忆集(卡表)方式记录,每个Region都要维护一个其他Region对自己内部对象的引用,Region中对象的可能被多个Region引用,所以G1的卡表实现要比CMS复杂很多

3.JVM参数配置

JVM开启G1配置可以通过-XX:+UserG1GC来配置

-verbose:gc -XX:+UseG1GC -Xms10M -Xmx10M  -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:SurvivorRatio=8

下面我们详细讲解下G1垃圾收集器的JVM参数配置

JVM参数参数描述
-XX:+UseG1GC开启使用G1垃圾收集器
-XX:ParallelGCThreads指定GC工作收集垃圾并发的线程数量
-XX:G1HeapRegionSize指定分区大小(1MB~32MB,必须2的N次幂),默认整堆划分2048个分区
-XX:MaxGCPauseMillis指定目标最大停顿时间(默认200ms)
-XX:G1NewSizePercent新生代比例的设定数值的下限(默认整堆5%,新生代最小值比例)
-XX:G1MaxNewSizePercent新生代比例的设定数值上限下限(最大60%,值配置整数)
-XX:TargetSurvivorRatioSurvivor区的填充容量(默认50%),Survivor区域里的一批对象(年龄1+年龄2+年龄n的多个年龄对象)总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代
-XX:MaxTenuringThreshold最大年龄阈值(默认15)
-XX:InitiatingHeapOccupancyPercent老年代占用空间达到整堆内存阈值(默认45%),Mixed GC周期结束后老年代使用率还是超过InitiatingHeapOccupancyPercent值,那么会再次触发全局并发标记过程,假如堆默认有100个region,如果有接近50个region都是老年代的region,50/100 > 45%的比例,则可能就要触发MixedGC了
-XX:G1MixedGCLiveThresholdPercent默认为 65%,G1会优先回收垃圾多的内存分段。垃圾占内存分段比例越高的,越会被先回收。并且由一个阈值决定内存分段是否被回收 -XX:G1MixedGCLiveThresholdPercent, 默认65%,意思是垃圾占内存分段比例要达到 65% 才会被回收。如果垃圾占比太低,意味着存活的对象占比高,在复制的时候会花费更多的时间,就不会处理这个Region
-XX:G1MixedGCCountTarget在一次回收过程中指定做几次筛选回收(默认8次),并发标记结束以后,老年代中100%为垃圾的 region 就直接被回收了,仅部分为垃圾的region会被分成8次回收(可以通过 -XX:G1MixedGCCountTarget 设置,默认阈值8),所以 Mixed GC 的回收集(CSet)包括八分之一的老年代内存分段、Eden 区内存分段、Survivor 区内存分段
-XX:G1HeapWastePercent默认值 10%,意思是允许整个堆内存有 10% 的空间浪费,意味着如果发现可以回收的垃圾占堆内存的比例低于10%,则不再进行混合回收,因为 GC 会花费很多的时间,但是回收到的内存却很少

这就是G1 垃圾收集器的基本原理和使用,我们这里只是讲解了基本的Region原理,分代收集,JVM参数等等,还有很多调优信息没了解,需要继续深入研究