【JVM】垃圾回收:垃圾收集器

一、语境中的并行与并发

并行

并行描述的时多条垃圾收集器线程之间的关系,说明同一时间有多条这样的线程在协同工作,通常默认此时用户线程是处于等待状态。

并发

并发描述的是垃圾收集器线程与用户线程之间的关系,说明同一时间垃圾收集器线程与用户线程都在运行。由于用户线程并未被冻结,所以程序仍然能响应服务请求,但由于垃圾收集器线程占用了一部分系统资源,此时应用程序的处理的吞吐量将受到一定影响。

二、串行收集器

  • 单线程: Serial 收集器(年轻代) + Serial Old 收集器(老年代)
  • 客户端模式的最好选择:适合个人电脑(适合CPU核数少的,多了也没用)、堆内存较小

在这里插入图片描述

三、吞吐量优先收集器

  • 多线程并行:Parallel Scavenge 收集器(年轻代) + Parallel Old 收集器(老年代)。jdk8默认使用
  • 吞吐量 = 运行用户代码时间 /(运行用户代码时间+运行垃圾收集时间【次数*单次时间,次数越多,时间越长】)
  • 适合堆内存较大,多核 cpu
  • 目标:尽可能让 单次GC的时间 最短。即单位时间内,STW的时间最短

在这里插入图片描述

四、响应时间优先收集器

  • 多线程并发:ParNew 收集器(年轻代) + CMS(Concurrent Mark Sweep)收集器(老年代)
  • 适合堆内存较大,多核 cpu
  • 目标:尽可能让 单次STW的时间 最短
  • 问题:
    1. “标记清除算法”:导致空间碎片化,无法存储大对象,进而导致”并发失败“。临时启用 SerialOld 收集器进行标记整理
    2. “并发标记、并发清理”:产生 “浮动垃圾”,需要预留空间。
    3. “重新标记”:先做新生代回收再重新扫描,减轻压力

在这里插入图片描述

五、G1收集器

JDK9 开始成为服务端模式下的默认垃圾收集器,CMS被声明为不推荐使用

  • 定义:Garbage First ,jdk9默认使用。

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

    • 用于老年代
    • 同时注重吞吐量(Throughput)和低延迟(Low latency),默认的暂停目标是 200 ms
    • 超大堆内存,会将堆划分为多个大小相等的 Region
    • 整体上是 标记+整理 算法,两个区域之间是 复制 算法。意味着G1运行期间不会产生内存空间碎片
  • 相关 JVM 参数

    • -XX:+UseG1GC :使用G1收集器
    • -XX:G1HeapRegionSize=size:设置堆内存每个区域大小
    • -XX:MaxGCPauseMillis=time:设置回收时最长停止时间

1)回收阶段

在这里插入图片描述

2)Young Collection

  • 将新对象分配到各个伊甸园区域

在这里插入图片描述

  • 当堆中伊甸园区域被逐渐被占满后,触发 Young Collection 并 STW 和 进行初始标记。将伊甸园区域幸存的对象使用复制的算法拷贝到幸存区域

在这里插入图片描述

  • 当堆中伊甸园区域被逐渐被占满、幸存区域中对象超过阈值后,触发 Young Collection 并 STW 和 进行初始标记。将伊甸园区域幸存的对象使用复制的算法拷贝到幸存区域,将幸存区域中没有超过阈值的对象拷贝到另一个幸存区域,将幸存区域中超过阈值的对象晋升到老年代区域

在这里插入图片描述

3)Young Collection + 并发标记
  • Young Collection:在 Young GC 时会进行 GC Root 的初始标记

  • 并发标记:老年代区域占用堆空间比例达到阈值时,进行 并发标记(不会 STW),由下面的 JVM 参数决定

    -XX:InitiatingHeapOccupancyPercent=percent (默认45%)

在这里插入图片描述

4)Mixed Collection

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

  • 最终标记(Remark)会 STW

  • 拷贝存活(Evacuation)会 STW会选择回收价值最高(获得的空间大小、回收所需时间)的区域进行回收,以满足暂停时间的设置

    -XX:MaxGCPauseMillis=ms

在这里插入图片描述

5)写屏障

在虚拟机层面对 “引用类型字段赋值” 这个动作的AOP切面。也就是说在赋值的前后增加相应代码执行额外的动作

  • 写后屏障:解决跨代引用问题,维护记忆集
  • 写前屏障:解决并发标记阶段的引用变更问题,并发扫描时的原始快照

6)跨代引用

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

  • 记忆集(Remembered Set)卡表(记忆集的实现)

  • 在引用变更时,更新脏卡:通过 post-write barrier**(写后屏障)** + dirty card queue

    • 异步更新:concurrent refinement threads 更新 Remembered Set

在这里插入图片描述

7)重新标记-Remark

pre-write barrier (写前屏障) + satb_mark_queue

  • 删除引用(原始快照):白色的C对象灰色的B对象 所引用,但在处理前被用户线程删除了灰色B对象的引用。
    • 异步更新:此时C对象 删除引用操作前的写前屏障将C对象加入到一个队列中,并将其标记为灰色
  • 插入引用:用户线程让 黑色的A对象 引用 白色的C对象
  • 重新标记阶段:STW,将队列中的对象取出并进行重新检测

在这里插入图片描述

8)DK8u20字符串去重

  • -XX:+UseStringDeduplication
  • 优点:节省大量内存
  • 缺点:略微多占用了 cpu 时间,新生代回收时间略微增加
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 内部,使用了不同的字符串表

9)DK8u40并发标记类卸载

  • -XX:+ClassUnloadingWithConcurrentMark: 默认启用

  • 所有对象都经过并发标记后,就能知道哪些类不再被使用

  • 卸载条件:当一个类加载器的所有类都不再使用,则卸载它所加载的所有类

10)DK8u60回收巨型对象

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

11)DK9并发标记起始时间的调整

  • 并发标记必须在堆空间占满前完成,否则退化为 FullGC
  • JDK 9 前:需要使用 -XX:InitiatingHeapOccupancyPercent
  • JDK 9 时:可以动态调整
    • -XX:InitiatingHeapOccupancyPercent 用来设置初始值
    • 进行数据采样并动态调整,总会添加一个安全的空档空间

12)DK9更高效的回收

  • 250+增强
  • 180+bug修复
  • https://docs.oracle.com/en/java/javase/12/gctuning

六、💡GC总结

  • SerialGC

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

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

    • 新生代内存不足发生的垃圾收集 - minor gc
    • 老年代内存不足发生的垃圾收集 - major gc
    • 并发清除失败导致老年代内存不足 - full gc
  • G1

    • 新生代内存不足发生的垃圾收集 - minor gc
    • 老年代内存达到一定比例 - mixed gc
    • 混合收集失败导致老年代内存不足 - full gc(多线程)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

愿你满腹经纶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值