G1收集器
- XX:+UseG1GC
G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对 **配备多颗处理器及大容量内存的机器,**以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征。
G1 收集器 在 JDK1.7 正式启用,是 JDK 9以后的默认垃圾收集器,取代了 CMS 以及 Parallel+Parallel Old 的组合,被 Oracle 官方称为“全功能的垃圾收集器”。
- 适合大内存机器,具备高吞吐量。
- 低 GC 停顿时间。
堆分布
区域分布
区分于传统的堆内存分布,G1 是将 JVM 堆内存划分为了多个 Region
,也就是多个相同大小的区域。
默认 Region 的大小是堆内存的 1/2048,因此如果设置堆内存大小为 4096MB,那么每个 Region 的大小为 2M。
年轻代中的Eden和Survivor对应的比例也跟之前一样,默认 8:1:1
。
假设年轻代现在有1000个region,eden区对应800个,s0对应100个,s1对应100个。
动态变化
在 G1 中年轻代和老年代都是多个 Region 的集合,并且 Region 的区域会动态变化,也就是一个 Region 本来是年轻代,GC 之后,可能会变为老年代。
大对象优化-Humongous
在 G1 中,有个对象区域是 Humongous
区域。专门用来存放大对象,解决了大对象在新生代和老年代之间的复制问题和大对象占用老年代空间导致的 fullGC问题。
大对象指的就是一个对象如果占用的空间超过了分区容量 50% 以上就称为大对象。
大对象的问题
默认大对象是直接放到老年代里面,但是会导致以下问题:
大对象复制
大对象复制会影响垃圾回收。
占用老年代空间,影响 fullGC
如果直接放到老年代,占用老年代空间,可能导致 fullGC频繁。
为了解决直接放入老年代产生的问题,G1 引入了 Humongous
区域。
如果一个 Humongous
区装不下一个巨型对象,那么 G1 会寻找连续的 Humongous
分区来存储。
垃圾回收
在 fullGC 过程,除了回收新生代和老年代,Humongous
区域同样会被回收。
Humongous
区域中的对象不会晋升到其它区域,是 G1 针对大对象做的特殊化处理。
需要注意的是,G1 除了针对大对象做了优化处理。像新生代的对象晋升规则依然生效的。Humongous区域的对象只会在自己区域进行垃圾回收。
GC过程
GC步骤
初始标记
STW。标记 GC Roots 对象能直接关联的对象,耗时很短。
并发标记
与 CMS一致。
与用户线程并发,通过 GC Roots 对象能直接关联的对象进行可达性分析,找出存活的对象,这个阶段耗时较长。
最终标记
与 CMS一致。
修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录。
筛选回收
STW。对各个 Region 的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间 来制定回收计划。
XX:MaxGCPauseMillis
指定预期 GC 时间
根据配置的预期 GC时间判断可以回收的 Region 数量进行回收,比如总共需要回收 1000 个 Region,此时
-XX:MaxGCPauseMillis=200
,通过之前的回收成本预测 200ms 只能回收 500 个 Region,那么本次垃圾回收只会回收 500个 Region。在用户控制的时间内,停顿用户线程,大幅提高垃圾回收的效率。在尽量降低用户线程停顿时间的前提下,对需要回收的 Region 按照价值和成本排序进行高效率回收。
回收算法
G1 回收算法主要是用的是标记-复制
算法。
- 将一个 Region 中的存活对象复制到另一个 Region 中。
- 清空原 Region 中的对象。
这种不会像 CMS 那样回收完因为有很多内存碎片还需要整理一次,G1采用复制算法回收几乎不会有太多内存碎片。
优先列表
G1 有计划地避免在整个 Java 堆中进行全区域的垃圾收集,G1跟踪各个 Region的垃圾堆积的价值大小。在后台维护一个优先级列表,每次根据允许的收集时间,优先回收价值最大的Region,G1侧重于回收垃圾最大量的区间,因此称之为 Garbage-First
垃圾优先。
这样能保证 G1 在有限停顿时间内尽量高效率的回收 Region。
G1垃圾收集分类
YoungGC
YoungGC 并不是说现有的 Eden 区放满了就会马上触发,G1 会计算下现在 Eden 区回收大概要多久时间,如果回收时间远远小于参数 -XX:MaxGCPauseMills
设定的值,那么增加年轻代的region,继续给新对象存放,不会马上做 YoungGC。
直到下一次Eden区放满,G1计算回收时间接近参数 -XX:MaxGCPauseMills
设定的值,那么就会触发 Young GC。
MixedGC
不是 FullGC,老年代的堆占有率达到参数 -XX:InitiatingHeapOccupancyPercent
设定的值则触发,回收所有的 Young 和部分 Old (根据期望的GC停顿时间确定old区垃圾收集的优先顺序)以及大对象区,正常情况G1的垃圾收集是先做 MixedGC
,主要使用复制算法,需要把各个region 中存活的对象拷贝到别的 region 里去,拷贝过程中如果发现没有足够的空 region 能够承载拷贝对象就会触发一次 Full GC
。
Full GC
停止系统程序,然后采用单线程进行标记、清理和压缩整理,好空闲出来一批 Region 来供下一次 MixedGC
使用,这个过程是非常耗时的。(Shenandoah优化成多线程收集了)
G1的特点
并行与并发
G1 利用到多核CPU来缩短 STW 的时间。在 GC 过程中部分阶段允许与用户线程并发执行。
分代收集
虽然 G1 引入了 Region 的概念管理堆,但是整体上还存在分代的概念。比如新生代和老年代,只不过这些代是按照比例以 Region 集合组成的。
空间整合
与 CMS的
标记-清理
不同,G1 采用的算法整体上看是标记-整理
,但是从局部上看是标记-复制
。可预测的停顿时间
这是 G1 的优势,可以通过指定配置 GC的时间,达到用户期盼的 GC 耗时。
XX:MaxGCPauseMillis
默认值是 200ms,不推荐非常低。不然会导致单次垃圾回收的 Region 数量太少。可能导致回收效率比新增效率慢,最终导致频繁 fullGC。
G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能在使用者指定的时间范围内完成垃圾回收。
G1参数设置
常见参数
XX:+UseG1GC
:使用G1收集器XX:ParallelGCThreads
:指定GC工作的线程数量XX:G1HeapRegionSize:
指定分区大小(1MB~32MB,且必须是2的N次幂),默认将整堆划分为2048个分区XX:MaxGCPauseMillis
:目标暂停时间(默认200ms)XX:G1NewSizePercent
:新生代内存初始空间(默认整堆5%)XX:G1MaxNewSizePercent
:新生代内存最大空间XX:TargetSurvivorRatio
: Survivor区的填充容量(默认50%),Survivor区域里的一批对象(年龄1+年龄2+年龄n的多个年龄对象)总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代XX:MaxTenuringThreshold
:最大年龄阈值(默认15)XX:InitiatingHeapOccupancyPercent
:老年代占用空间达到整堆内存阈值(默认45%),则执行新生代和老年代的混合收集(MixedGC),比如我们之前说的堆默认有2048个region,如果有接近1000个region都是老年代的region,则可能就要触发MixedGC了XX:G1MixedGCLiveThresholdPercent
(默认85) region中的存活对象低于这个值时才会回收该region,如果超过这个值,存活对象过多,回收的的意义不大。XX:G1MixedGCCountTarget
:在一次回收过程中指定做几次筛选回收(默认8次),在最后一个筛选回收阶段可以回收一会,然后暂停回收,恢复系统运行,一会再开始回收,这样可以让系统不至于单次停顿时间过长。XX:G1HeapWastePercent
(默认5%): gc过程中空出来的region是否充足阈值,在混合回收的时候,对Region回收都是基于复制算法进行的,都是把要回收的Region里的存活对象放入其他Region,然后这个Region中的垃圾对象全部清理掉,这样的话在回收过程就会不断空出来新的Region,一旦空闲出来的Region数量达到了堆内存的5%,此时就会立即停止混合回收,意味着本次混合回收就结束了。
Kafka的参数设置
-XX:+UseG1GC
-XX:MaxGCPauseMillis=20
-XX:InitiatingHeapOccupancyPercent=35
-XX:+DisableExplicitGC