JVM Garbage First 垃圾回收器

本文详细介绍了JVM的G1垃圾回收器,包括Young Collection、Mixed Collection、Full GC等过程,强调了低延迟和吞吐量的重要性。G1采用了标记整理算法,使用卡表和Remembered Set提高效率,JDK不同版本还引入了字符串去重、并发标记类卸载和巨型对象回收等优化策略。
摘要由CSDN通过智能技术生成

JVM Garbage First 垃圾回收器

定义:Garbage First

  • 2004论文发布
  • 2009 JDK 6u14 体验
  • 2012 JDK 7u4 官方支持
  • 2017 JDK 9 默认

使用场景:

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

相关JVM参数

-XX:+UseG1GC

-XX:G1HeapRegionSize=size:堆内存Region大小

-XX:MaxGCPauseMillis=time:暂停目标时间

在这里插入图片描述

1、Young Collection

会发生 STW(Stop The World)

  • 初始阶段,新生成的对象会进入伊甸园

在这里插入图片描述

  • 在发生垃圾回收时,伊旬园中的未被回收的对象会进入幸存区

在这里插入图片描述

  • 再次发生垃圾回收时,伊旬园中的未被回收的新对象会复制到一个新的幸存区(Region块),并将旧幸存区中达到指定阈值的对象复制到老年代,没有被回收且没有达到阈值的对象复制到新的幸存区

在这里插入图片描述

2、Young Collection + CM

  • 在 Young GC 会进行 GC Root 的初始标记
  • 老年代占用堆空间比例达到阈值时,进行并发标记(不会 STW),由下面的 JVM 参数决定
-XX:InitiatingHeapOccupancyPercent=percent (默认为45%)

在这里插入图片描述

3、Mixed Collection

会对伊甸园、幸存区、老年代进行全面的垃圾回收

  • 最终标记(Remark)会发生 STW
  • 拷贝存货(Evacuation)会发生 STW
-XX:MaxGCPauseMillis=ms

在此阶段伊甸园中的未被回收的对象会复制到幸存区中,同时幸存区中也会进行垃圾回收,将幸存区中未被回收的对象复制到新的幸存区中,寿命到达指定阈值的对象复制到老年代中。在回收老年代时也是同样使用复制算法,并根据设置的最大暂停时间,优先回收其中回收价值最高的部分老年代中的对象。

在这里插入图片描述

4、Full GC

  • Serial GC
    • 新生代内存不足发生的垃圾回收:Minor GC
    • 老年代内存不足发生的垃圾回收:Full GC
  • Parallel GC
    • 新生代内存不足发生的垃圾回收:Minor GC
    • 老年代内存不足发生的垃圾回收:Full GC
  • CMS
    • 新生代内存不足发生的垃圾回收:Minor GC
    • 到老年代所占空间达到设置的阈值时,会触发并发标记后混合回收,当并发垃圾回收失败时就会发生Full GC
  • G1
    • 新生代内存不足发生的垃圾回收:Minor GC
    • 到老年代所占空间达到设置的阈值时,会触发并发标记后混合回收,此时,垃圾回收的速度小于垃圾产生的速度时,就会发生Full GC

5、Young Collection 跨带引用

  • 新生代回收的跨代引用(老年代引用新生代)

  • 在新生代中进行垃圾回收时,要找到根对象(GC Root)进行可达性分析,而部分新生代中对象的根对象(GC Root)时在老年代中的。而遍历老年代查找根对象效率较低,因此使用卡表(Card Table)将老年代细分为卡(Card)组成的卡表(Card Table),当老年代中的对象引用了新生代中的对象,则会把老年代中对应的卡(Card)标记为脏卡(Dirty Card)。此时,在老年代中的对象引用新生代中的对象后,进行垃圾回收时就不必遍历整个老年代,提升垃圾回收的效率。

  • 在新生代对象中有一个Remembered Set,记录着老年代中引用此新生代对象的脏卡(Dirty Card),在进行垃圾回收时就可以通过Remembered Set找到对应的脏卡(Dirty Card),然后通过脏卡(Dirty Card)遍历根对象(GC Root)。

  • 在对象引用变更时通过写后屏障(Post-Write Barrier)和脏卡队列(Dirty Card Queue)

  • Remembered Set通过Concurrent Refinement Threads更新

在这里插入图片描述

6、Remark

在垃圾回收器进行并发标记阶段时,会在可以回收的对象上加入写屏障(Pre-Write Barrier),以防对象在标记后发生引用变化后依旧清除对象。

在这里插入图片描述

在这里插入图片描述

当添加写屏障(Pre-Write Barrier)的对象发生引用变化之后,写屏障会将对象加入satb_mark_queue,在重新标记(Remark)时(会发生 STW)就会重新判断satb_mark_queue中的对象是否可以被清除。

在这里插入图片描述

7、JDK 8u20 字符串去重

  • 优点:节省大量内存
  • 缺点:略微多占用了cpu时间,新生代回收时间略微增加
-XX:UseStringDeduplication
String s1 = new String("hello"); //char[]{'h', 'e', 'l', 'l', 'o'}
String s2 = new String("hello"); //char[]{'h', 'e', 'l', 'l', 'o'}
  • 将所有新分配的字符串放入一个队列
  • 当新生代回收时,G1并发检查是否有字符串重复
  • 如果它们值一样,让它们引用同一个char[]

注意:此字符串去重与String.intern()不同,String.intern()关注的是字符串对象而字符串去重关注的是char[]在JVM内部,使用了不同的字符串表。

8、JDK 8u40 并发标记类卸载

所有对象都经过并发标记后,就知道哪些类不再被使用,当一个类加载器的所有类都不在使用,则卸载它所加载的所有类

-XX:+ClassUnloadingWithConcurrentMark // 默认启用

9、JDK 8u60 回收巨型对象

  • 一个对象大于Regin的一半时,称之为巨型对象
  • G1不会对巨型对象进行拷贝
  • 回收时被优先考虑
  • G1会跟踪老年代所有incoming引用,这样老年代incoming引用为0的巨型对象就可以在新生代垃圾回收时处理掉

在这里插入图片描述

10、JDK 9 并发标记起始时间调整

  • 并发标记必须在堆空间占满前完成,否则退化为Full GC
  • JDK 9 之前需要使用-XX:InitiatingHeapOccupancyPercent
  • JDK 9 可以动态调整
    • -XX:InitiatingHeapOccupancyPercent 用来设置初始值
    • 进行数据采样并动态调整
    • 总会添加一个安全的空档空间
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

陌尘吖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值