JVM(十六) -- 内存与垃圾回收(五) -- 垃圾回收(四) -- 垃圾回收器

    技术2022-07-10  168

    1.GC分类与性能指标

    1.1 垃圾回收器概述

    垃圾回收器没有在规范中进行过多的规定,可以由不同的厂商、不同版本的JVM来实现由于JDK的版本处于高速迭代过程中,因此Java发展至今已经衍生了众多的GC版本从不同角度分析垃圾收集器,可以将GC分为不同的类型

    1.2 垃圾回收器分类

    1.2.1 按线程数分,可以分为串行垃圾回收器和并行垃圾回收器

    1.2.2 按工作模式分,可以分为并发式垃圾回收器和独占式垃圾回收器

    1.2.3 按碎片处理方式分,可分为压缩式垃圾回收器和非压缩式垃圾回收器
    压缩式垃圾回收器会在回收完成后,对存活对象进行压缩整理,消除回收后的碎片 再分配对象空间使用:指针碰撞 非压缩式的垃圾回收器不进行这步操作 再分配对象空间使用:空闲列表
    1.2.4 按工作的内存区间分,可分为年轻代垃圾回收器和老年代垃圾回收器

    1.3 评估GC的性能指标

    吞吐量:运行用户代码的时间占总运行时间的比例 总运行时间:程序的运行时间+内存回收的时间 垃圾收集开销:吞吐量的补数,垃圾收集所用时间与总运行时间的比例暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间收集频率:相对于应用程序的执行,收集操作发生的频率内存占用:Java堆区所占的内存大小快速:一个对象从诞生到被回收所经历的时间

    上述标黄的三项是主要的性能指标,这三者共同构成一个“不可能三角”。三者总体的表现会随着技术进步而越来越好。一款优秀的收集器通常最多同时满足其中的两项

    这三项中,暂停时间的重要性日益凸显。因为随着硬件发展,内存占用多些越来越能容忍,硬件性能的提升也有助于降低收集器运行时对应用程序的影响,即提高了吞吐量。而内存的扩大,对延迟反而带来负面效果。

    简单来说,主要抓住2点:吞吐量,暂停时间。

    1.3.1 吞吐量

    1.3.2 暂停时间

    1.3.3 吞吐量和暂停时间对比:

    2.不同的垃圾回收器概述

    2.1 垃圾回收器发展史

    2.2 七款经典垃圾回收器

    七种经典垃圾回收器和垃圾分代之间的关系

    垃圾回收器的组合关系

    如何查看默认回收器

    -XX:+PrintCommandLineFlags:查看命令行相关参数(包含使用的垃圾回收器)使用命令行指令:jinfo -flag 相关垃圾回收器参数 进程ID

    3.Serial回收器:串行回收

    4.ParNew回收器:并行回收

    5.Parallel回收器:吞吐量优先

    6.CMS回收器:低延迟

    注意:重新标记的是之前怀疑是垃圾的垃圾,如果在并发标记阶段新生成的垃圾(原来没有被怀疑是垃圾),重新标记是不会标记的 。

    CMS收集器可以设置的参数

    7.G1回收器:区域划分代式

    7.1 既然有了前面几个强大的GC,为什么还要发布Garbage First GC?

    7.2 为什么名字叫做Garbage First?

    我的理解:G1看中的是回收效率,即使有个region快满了,然后去回收发现几乎无法回收,都是可达对象,不能产生空闲内存,这样就浪费了时间。

    7.3 G1概述

    7.4 G1优点:

    7.5 G1缺点:

    7.6 G1参数设置

    最大GC暂停时间不能设置的太短,如果太短,G1回收器能够回收的region就会更少,就会导致很多region永远不会回收,最后内存占满执行Full GC。

    7.7 G1常见操作步骤

    7.8 G1的适用场景

    7.9 Region:化整为零

    7.10 G1垃圾回收过程:

    主要包含以下三个环节:

    年轻代GC(Young GC),对年轻代的回收,是回收年轻代所有的region,包括所有的eden区和survivor区的region老年代并发标记过程(Concurrent Marking)混合回收(Mixed GC)(如果需要,单线程、独占式、高强度的Full GC还是继续存在的。它针对GC的评估失败提供了一种失败保护机制,即强力回收。)

    上述例子的每45秒重新分配2G内存是指每45秒进行一次全年轻代的YGC,每31小时进行mixed GC。

    7.10.1 Remembered Set 记忆集

    一个对象被不同区域引用的问题:

    一个Region不可能是鼓励的,一个Region中的对象可能被其他任意Region中的对象引用。判断对象存活时,是不是要扫描整个堆区才能保证准确?在其他的分代收集器,也存在这样的问题(但是G1更突出)一个老年代的region的对象引用一个年轻代的region的对象,是不是进行YGC的时候同时也要扫描老年代?这样的话会降低YGC的效率。

    解决方法: 我的理解:每次进行引用的时候,都会在region自己的Rset中记录下,是哪个region中的对象引用了当前region中的对象。每次对当前region进行回收时,在GC Roots中假如这个Rset,如果Rset中有引用就不能被回收。(注意,这里是我的简单理解,引用时并不是直接记录到Rset中,而是记录到Card中)

    Write Barrier:写屏障 CardTable:卡表

    7.10.2 回收过程一:年轻代GC

    dirty card queue:

    7.10.3 回收过程二:并发标记过程(针对老年代)

    7.10.4 回收过程三:混合回收

    回收过程四:Full GC

    7.11 G1优化建议:

    8.垃圾回收器总结:

    8.1 怎么选择垃圾回收器?

    9.GC日志分析

    9.1 jdk8下PrintGC的日志:

    9.2 jdk8下PrintGCDetails的日志:

    9.3 补充说明

    YGC: Full GC:

    9.4 日志分析工具:

    导出日志:

    10.垃圾回收器的新发展

    10.1 Open JDK12的Shenandoah GC:

    12.2 ZGC

    Processed: 0.028, SQL: 12