JVM第八篇(垃圾回收器G1)

垃圾回收器G1
定义:Garbage First

发展:
2004 论文发布
2009 JDK 6u14 体验
2012 JDK 7u4 官方支持
2017 JDK 9 默认G1,废弃了CMS

适用场景:

  1. 同时注重吞吐量(Throughput)和低延迟(Low latency),默认的暂停目标是 200 ms
  2. 超大堆内存,会将堆划分为多个大小相等的 Region
  3. 整体上是 标记+整理 算法,两个区域之间是 复制 算法

相关 JVM 参数
-XX:+UseG1GC (使用G1垃圾回收器,JDK9以上为默认)
-XX:G1HeapRegionSize=size (设置Region的个数)
-XX:MaxGCPauseMillis=time (设置最大暂停时间,默认为200ms)

G1垃圾回收器的回收阶段。
请添加图片描述
G1的垃圾回收阶段主要分为三个阶段:
youngCollection 是对新生代的垃圾回收。
youngCollection+Concurrent Mark 对新生代的垃圾回收,同时执行一些并发的标记。
MixedCollection 混合的垃圾回收。

三个阶段为一个循环的过程:在刚开始,新生代的内存满了之后,执行一次 youngCollection 对新生代的垃圾进行回收,经过一段时间,老年代的内存占用达到一个阈值,会在执行youngCollection的同时,执行一些并发标记,然后会进行一次MixedCollection,对新生代,幸存区,老年代做一次垃圾回收,然后进入下一个循环的过程。

G1垃圾回收器,将堆内存划分为多个Region区域。每个Region都可以作为独立的伊甸园(E),幸存区(S),和老年代(O)

youngCollection的执行过程:
在进行新生代的垃圾回收过程中,会暂停所有的用户线程(STW)。

  1. 在堆中存在多个Region区域作为伊甸园区(E)
    请添加图片描述
  2. 伊甸园区(E)内存满了之后,进行一次youngCollection,将伊甸园区中垃圾对象标记出来,进行垃圾回收,将幸存对象存放到幸存区(S)(两个区域之间采用的是复制算法)。
    请添加图片描述
  3. 经过一段时间,当幸存区的内存也已经占满,再次进行youngCollection,将伊甸园中内存进行垃圾回收,将幸存对象存入幸存区,将幸存区的对象进行垃圾回收,并进行寿命标记,如果存在有达到阈值的对象,则将其存入老年代。
    请添加图片描述
    youngCollection+Concurrent Mark执行过程:
    在进行垃圾回收时,会对对象进行初始标记和并发标记(CM),初始标记是对GC root对象进行标记,并发标记是从GC root对象出发,顺着引用链,标记其它对象。

在youngCollection时,会进行初始标记。并发标记是根据老年代中内存的占比达到阈值时,进行并发标记。在进行并发标记时,不会暂停用户的线程(没有STW时间)。
设置并发标记的参数,默认老年代内存占堆内存45%时,进行一次并发标记。
-XX:InitiatingHeapOccupancyPercent=percent (默认45%)。
请添加图片描述
MixedCollection :
混合回收:会对 伊甸园区E,幸存区S,老年代区O,全面的进行垃圾回收。
最终标记(Remark)会 STW
拷贝存活(Evacuation)会 STW

在伊甸园区,进行垃圾回收,将幸存对象存放到幸存区,对幸存区进行垃圾回收,对幸存对象进行寿命标记,达到阈值的存入老年代。
对老年代中进行垃圾回收,将老年代最具有回收价值的区域(即老年代区中存在大量垃圾对象,回收能得到最大的空闲内存)中的幸存对象复制到新的老年代区中。
下图选择了两个最具有价值的老年代区,复制到新的老年代区中。
这样做的目的是为了满足最大暂停时间(STW时间),在满足STW时间的条件下,优先回收垃圾最多的区。

请添加图片描述

Full GC 与 Minor GC:
对应下面四种不同的垃圾回收器,Minor GC和Full GC 发生的情况。
SerialGC
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足发生的垃圾收集 - full gc

ParallelGC
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足发生的垃圾收集 - full gc

对于 SerialGC和ParallelGC来说,进行新生代垃圾回收时,就叫做 Minor GC,进行老年代垃圾回收时,就叫做Full GC

CMS
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足:分为两种情况
在并发回收过程中,叫 Minor GC。
在并发回收失败后,退化为SerialGC,则叫 Full GC。

G1
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足:分为两种情况
如果 youngCollection + ConcurrentMark和MixedCollection这两个垃圾回收阶段,如果垃圾回收的速度比用户线程垃圾产生的速度快/慢,则还是 Minor GC / Full GC。(当并发进行垃圾回收时,产出的垃圾速度大于回收垃圾的速度,则退化为SerialGC,为Full GC)

Young Collection 跨代引用:
新生代跨代引用(老年代中引用新生代对象)问题:
在进行新生代的垃圾回收时,先找到 GC root对象,然后进行可达性分析,找到幸存对象,将幸存对象复制到幸存区中,将垃圾对象回收,完成新生代的垃圾回收。

存在的问题:如果根对象来自老年代区域,老年代,对象多,遍历起来效率低。
这里采用卡表与 Remembered Set,解决遍历老年代,效率低的问题。

将老年代区域分为若干个Card ,每个Card为512B,如果某一个Card中对象引用了某一个新生代的对象,那么在对应的卡表中将 Card 标记为脏Card。

老年代会采用一个卡表,标记脏Card区域。在新生代中,有一个 Remembered Set 记录有哪些脏卡引用了新生代中存在的对象。

通过卡表和 Remembered Set,在对新生代进行垃圾回收时,在寻找 GC root 时,直接到对应的 Card区域寻找,大大提高了效率。

在每次对象的引用变更时通过 post-write barrier将对应的卡标记为脏卡,由于更新是异步的,将对应的更新指令放在dirty card queue中,将来由一个线程完成更新操作。请添加图片描述

Remark(重新标记阶段):
在重新标记之前,并发标记阶段,由于垃圾回收线程与用户线程并发执行,在垃圾回收线程标记完对象引用的状态后,用户线程有可能会改变标记完成的对象的引用,这就需要在重新标记阶段,将已经改变了的对象,重新标记。

pre-write barrier 如果对象的引用发生了变化,写屏障指令就会被执行。写屏障指令会把对象加入satb_mark_queue这个队列中去。
到了重新标记阶段,将所有用户线程暂停,将satb_mark_queue队列中已经被改变引用的对象,重新标记。

字符串去重:
对于 下面的代码:

String s1 = new String("hello"); // char[]{'h','e','l','l','o'}
String s2 = new String("hello"); // char[]{'h','e','l','l','o'}

在运行过程中,创建了两个值相同的对象存放在堆中。
G1对这种情况做了优化操作,完成字符串的去重功能。

  1. 在创建字符串时,将所有新创建的字符串对象,加入到一个队列中。
  2. 在进行新生代垃圾回收时,将扫描队列判断是否有字符串重复,如上面代码,s1和s2引用对象的值重复,则可以进行优化操作,让s2引用s1的对象。然后s2原来引用的对象就没有引用,在新生代垃圾回收时,进行回收。

优点:节省大量内存
缺点:略微多占用了 cpu 时间,新生代回收时间略微增加
开启字符串去重功能的VM 参数:-XX:+UseStringDeduplication

并发标记类卸载:

所有对象都经过并发标记后,就能知道哪些类不再被使用,当一个类加载器的所有类都不再使用,则卸载它所加载的所有类。(类信息和类加载器在方法区中)
-XX:+ClassUnloadingWithConcurrentMark 默认启用

回收巨型对象:
一个对象大于 region 的一半时,称之为巨型对象
巨型对象区(humongous region):可能会占用多个Region请添加图片描述
G1 不会对巨型对象进行拷贝
回收时被优先考虑
G1 会跟踪老年代所有 incoming 引用,这样老年代 incoming 引用为0 的巨型对象就可以在新生
代垃圾回收时处理掉。

并发标记起始时间的调整:
并发标记必须在堆空间占满前完成,否则退化为 FullGC,垃圾回收速度跟不上垃圾产生速度会退化为FullGC。
为了尽可能的避免 FullGC,可以设置 -XX:InitiatingHeapOccupancyPercent 参数(堆内存使用占比参数),达到这个值,将会进行并发标记,然后进行 Mixed GC。
在 JDK 9 可以自动进行动态调整
-XX:InitiatingHeapOccupancyPercent 用来设置初始值
进行数据采样并动态调整,尽可能的早点进行并发标记,保留一个安全的空档空间,确保堆内存足够,避免Full GC

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值