【JVM-4】G1垃圾回收器


接上篇的垃圾收集器~

一、G1(Garbage First)

2012 JDK 7u4官方支持
2017 JDK9默认

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

1. G1垃圾回收阶段概述

  1. 新生代垃圾收集
  2. 新生代垃圾收集+并发标记(CM)
  3. 混合收集
    不断循环 当老年代内存超过一定阈值,会进行并发标记;进入混合收集,就是对新生代和老年代都进行一次规模较大的垃圾收集。
    在这里插入图片描述

2. Young Collection 新生代垃圾回收

在这里插入图片描述
堆被划分为多个大小相等的Region,每个区域都可以独立作为Eden、幸存区、老年代。
类加载时创建的对象会分配到Eden区中,当区域逐渐占满,会触发一次新生代垃圾回收,STW。

以拷贝算法放入幸存区:
在这里插入图片描述
再工作一段时间,当幸存区的对象也比较多了,对象存活年龄超过一定时间,,又会触发新生代垃圾回收,幸存区一部分对象会晋升到老年代,不够年龄的会拷贝到另一个幸存区:
在这里插入图片描述

3. Young Collection+ CM

CM:并发标记
初始标记是标记根对象

  • Young GC 会进行GC Root的初始标记
  • 老年代占用堆空间比例达到阈值时,进行并发标记(不会STM),由一下JVM参数决定
    -XX:InitiatingHeapOccupancyPercent=percent (默认45%)
    在这里插入图片描述

4. Mixed Collection

会对E,S,O进行全面垃圾回收。

会挑出老年代中回收价值最高的几个区域,这样复制的区域少了,需要的gc时间就少了。(优先收集垃圾最多的区域)

最终标记(Remark)会 STW
拷贝存活(Evacuation)会 STW

在这里插入图片描述
和CMS类似,并发失败了以后才算full GC。当收集速度比不上垃圾产生速度时,退化为串行GC(才会叫做full gc)

5. 跨代引用

老年代引用新生代的问题:
将老年代的区域再进行细分(CardTable),一个card引用了新生代,那么标记为脏card。这样gc的时候找gc root不用遍历整个老年代,只要关注脏card即可。 减少搜索范围,提高效率。
在这里插入图片描述
以下示意图:粉色部分为脏卡,记录在Remembered Set(新生代)内;在脏卡区遍历gc root
标记脏卡:在每次对象引用变更时,都要更新卡表中的卡。这不是立刻更新的,而是将更新指令放在一个队列中,将来用一个线程去执行

在引用变更时通过 post-write barrier + dirty card queue
concurrent refinement threads 更新 Remembered Set

在这里插入图片描述

6. remark (重新标记阶段)

下面这张图是并发标记阶段,对象的处理状态。
黑色是已经处理完成的,结束时会被保留的对象
灰色是正在处理中的
白色是尚未处理的
最后全部处理完毕以后,没有被引用的会被当成垃圾。
在这里插入图片描述
写屏障 pre-write barrier 技术,在对象引用改变前加入一个队列(satb_mark_queue) 最后remark阶段配合这个队列进行进一步判断。

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'}

//让这两个字符串对象都引用同一个char[]数组

将所有新分配的字符串放入一个队列
当新生代回收时,G1并发检查是否有字符串重复
如果它们值一样,让它们引用同一个 char[]

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

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

对垃圾回收器的功能增强
可以把类都卸载掉!!
所有对象都经过并发标记后,就能知道哪些类不再被使用,当一个类加载器的所有类都不再使用,则卸载它所加载的所有类
-XX:+ClassUnloadingWithConcurrentMark 默认启用

9. JDK 8u60 回收巨型对象

  • 一个对象大于 region 的一半时,称之为巨型对象
  • G1 不会对巨型对象进行拷贝(毕竟效率低 不会标记-复制,只是计算引用)
  • 回收时被优先考虑
  • G1 会跟踪老年代所有 incoming 引用,这样老年代 incoming 引用为0 的巨型对象就可以在新生代垃圾回收时处理掉

10. JDK9并发标记起始时间调整

JDK9之前设置参数,老年代占堆内存的大小达到45%阈值后,并发垃圾回收开始。
JDK9之后可以动态调整阈值,这个参数-XX:InitiatingHeapOccupancyPercent用来设置初始值,在之后过程中会数据采样并动态调整,总会添加一个安全的空档空间,让堆的空间足够大,能够容纳浮动垃圾。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值