深入理解Java虚拟机系列 --12 垃圾回收篇03 --常用的垃圾回收器详解

  

因为热爱所以坚持,因为热爱所以等待。熬过漫长无戏可演的日子,终于换来了人生的春天,共勉!!!

目录

1.垃圾回收器分类

(1).按线程数分类

(2).按工作模式分

(3).按照碎片处理方式

(4).按工作内存区间

2.评估GC的性能指标

3.垃圾回收器发展历程

②.七款经典的垃圾回收器

③.七款经典收集器与垃圾分代的关系

④.垃圾回收器的组合关系

4.7款垃圾回收器详解

(1). Serial、SerialOld 回收器:串行回收(了解)

(2).ParNew回收器:并行回收(了解)

(3). Parallel、ParallelOld:吞吐量优先

(4).CSM回收器:低延迟

(5).G1垃圾收集器

(6).7款垃圾回收器总结

(7).怎么选择垃圾回收器?

5.GC日志分析

6.其他的GC

①革命性的ZGC

②.AliGC


1.垃圾回收器分类

(1).按线程数分类

可以分为串行垃圾回收器(Serial)和并行垃圾回收器(Parallel)

①.串行回收是在同一时间段内只允许有一个CPU执行回收操作,此时工作线程被暂停,直至垃圾回收工作结束

  • 在诸如单CPU处理器或者较小的应用内存等硬件平台不是特别优越的
    场合,串行回收器的性能表现可以超过并行回收器和并发回收器。所
    以,串行回收默认被应用在客户端的Client模式下的JVM中

②.和串行回收相反,并行收集可以运用多个CPU同时执行垃圾回收,因此提升
了应用的吞吐量,不过并行回收仍然与串行回收-一样,采用独占式,使用
了“Stop-the-world"机制。

(2).按工作模式

可以分为并发垃圾回收器(Concurrent)和独占式垃圾回收器(STW)

①独占式垃圾回收器(Stop the world)- - 旦运行,就停止应用程序中的所有用
户线程,直到垃圾回收过程完全结束。

②并发式垃圾回收器与应用程序线程交替工作,以尽可能减少应用程序的停顿时间。

(3).按照碎片处理方式

可分为压缩式垃圾回收器和非压缩式垃圾回收器

  • 压缩式垃圾回收器会在回收完成后,对存活对象进行压缩整理,消除回收后的碎片。
  • 非压缩式的垃圾回收器不进行这步操作。|
     

(4).按工作内存区间

分为年轻代垃圾回收老年代垃圾回收

2.评估GC的性能指标

①.吞吐量:运行用户代码的时间占总运行时间的比例

吞吐量 = 运行用户代码 / (运行用户代码时间 + 垃圾收集时间)

例如:6秒内,STW = 0.4s ,用户线程为 5.6s , 吞吐量 = 5.6 / 6 = 93.33%

吞吐量优先,意味着在单位时间内,STW的时间最短

在这里插入图片描述

②.垃圾收集开销:垃圾收集所用时间占总运行时间的比例, 等于1 - 吞吐量

垃圾收集开销 = 垃圾收集时间 / (运行用户代码时间 + 垃圾收集时间)

③.暂停时间执行垃圾收集时, 程序的工作线程被暂停的时间

暂停时间优先,意味着尽可能让单次STW的时间最短

在这里插入图片描述

④.收集频率: 相对与程序执行,收集操作发生的频率

⑤.内存占用:Java堆所占的内存大小

吞吐量、暂停时间、内存占用这三者共同构成-一个“不可能三角”。三者总体的表现会随着技术进步
而越来越好。一款优秀的收集器通常最多同时满足其中的两项。

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

  • 高吞吐量较好因为这会让应用程序的最终用户感觉只有应用程序线程在做“生产性”工作。直觉上,吞吐量越高程序运行越快。.
  • 低暂停时间(低延迟)较好因为从最终用户的角度来看不管是GC还是其他原因导致一个应用被挂起始终是不好的。这取决于应用程序的类型,有时候甚至短暂的200毫秒暂停都可能打断终端用户体验。因此,具有低的暂停时间是非常重要的,特别是对于一个交互式应用程序
  • 不幸的是”高吞吐量”和”低暂停时间”是- -对相互竞争的目标(矛盾)

        ➢因为如果选择以吞吐量优先,那么必然需要降低内存回收的执行频率,但是这样会导致GC需要更长的暂停时间来执行内存回收。
        ➢相反的,如果选择以低延迟优先为原则,那么为了降低每次执行内存回收时的暂停时间,也只能频繁地执行内存回收,但这又引起了年轻代内存的缩减和导致程序吞吐量的下降。

现在标准:在最大吞吐量优先的情况下,降低停顿时间

3.垃圾回收器发展历程

①. 垃圾收集器发展史

  • 1999年随JDK1.3.1一起来的是串行方式的Serial GC ,它是第一款GC。ParNew垃圾收集器是Serial收集器的多线程版本
  • 2002年2月26日,Parallel GC 和Concurrent Mark Sweep GC(CMS)跟随JDK1.4.2一起发布Parallel GC在JDK6之后成为HotSpot默认GC
  • 2012年,在JDK1.7u4版本中,G1可用。(-XX:+UseG1GC)
  • 2017年,JDK9中G1变成默认的垃圾收集器,以替代CMS
  • 2018年3月,JDK 10中G1垃圾回收器的并行完整垃圾回收,实现并行性来改善最坏情况下的延迟
  • 2018年9月,JDK11发布。引入Epsilon 垃圾回收器,又被称为"No-Op(无操作)"回收器。同时,引入ZGC:可伸缩的低延迟垃圾回收器(Experimental)
  • 2019年3月,JDK12发布。增强G1,自动返回未用堆内存给操作系统。同时,引入Shenandoah GC:低停顿时间的GC(Experimental)
  • 2019年9月,JDK13发布。增强ZGC,自动返回未用堆内存给操作系统
  • 2020年3月,JDK14发布。删除CMS垃圾回收器扩展ZGC在macOS和Windows上的应用

②.七款经典的垃圾回收器

  • 串行回收器:Serial、Serial Old
  • 并行回收器:ParNew、Parallel、Scavenge、Parallel Old
  • 并发回收器:CMS、G1

③.七款经典收集器与垃圾分代的关系

在这里插入图片描述

  •  新生代收集器: Serial GC、ParNew GC、Parallel Scavenge GC
  • 老年代收集器: Serial 0ld GC、 Parallel 0ld GC、 CMS GC
  • 整堆收集器: G1 GC

④.垃圾回收器的组合关系

  •  两个收集器间有连线,表明它们可以搭配使用:Serial/Serial 01d、Serial/CMS、 ParNew/Serial 01d、ParNew/CMS、Parallel Scavenge/Serial 01d、Parallel Scavenge/Parallel 0ld、G1;
  • 其中Serial 0ld作为CMS 出现"Concurrent Mode Failure"失败的后 备预案
  • (红色虚线)由于维护和兼容性测试的成本,在JDK8时将Serial+CMSParNew+Serial old这两个组合声明为废弃,JDK8默认为Prrallel Scavenge + Parallel Od 也可以 ParNew + CMS 或者单CPU情况下使用 Serial +SerialOld)
  • JDK 9中完全取消了这些组合的支持,移除了Parallel Scavenge + Serial Old(绿色虚线)
  • JDK 14中:弃用Parallel Scavenge和Serial0ld GC组合青色虚线)
  • JDK 14中:删除CMS垃圾回收器

⑤.查看默认的垃圾收集器

1. VM option 加上 -xx:+PrintCommandLineFlags  查看命令行相关参数(包含使用的垃圾收集器)

/**  注意:测试环境为JKD8
 *  -XX:+PrintCommandLineFlags
 *  -XX:+UseSerialGC:表明新生代使用Serial GC ,同时老年代使用Serial Old GC
 *  -XX:+UseParallelGC:表明新生代使用Parallel GC
 *  -XX:+UseParallelOldGC:表明老年代使用Parallel Old GC
 *  -XX:+UseConcMarkSweepGC:表明老年代使用CMS GC。同时,年轻代会触发对ParNew 的使用
    一对的会相互触发
 */
public class GCUseTest {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();

        while(true){
            byte[] arr = new byte[100];
            list.add(arr);
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
输出:
(-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 
-XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops -XX:+UseParallelGC)

2.使用命令行指令: jinfo 一flag [垃圾回收器参数 ] [进程ID]

1.通过jps查看进程

2.使用jinfo - flag UseParallelGC 924

结果:

4.7款垃圾回收器详解

(1). Serial、SerialOld 回收器:串行回收(了解)

在这里插入图片描述

 ①. Serial收集器采用复制算法、串行回收和"Stop一 the一World"机制的方式执行内存回收

②. Serial 0ld收集器同样也采用了串行回收 和"Stop the World"机制,只不过内存回收算法使用的是标记一压缩算法

  • Serial old是运行在Client模式下默认的老年代的垃圾回收器
     
  • Serial old在Server模式下主要有两个用途:
    • ①与新生代的ParallelScavenge配合使用
    • ②作为老年代CMS收集器的后备垃圾收集方案

③. 单线程回收:使用一个cpu或一条线程去完成垃圾收集工作 | 必须暂停其他所有的工作线程

④. 使用 -XX: +UseSerialGC 参数可以指定年轻代和老年代都使用串行收集器
等价于新生代用Serial GC,且老年代用Serial 0ld GC
控制台输出:
-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseSerialGC

优势:

  • 简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,可以获得最高的单线程收集效率

       

(2).ParNew回收器:并行回收(了解)

 在这里插入图片描述

 ①. 如果说Serial GC是年轻代中的单线程垃圾收集器,那么ParNew收集器则是Serial收集器的多线程版本

②. ParNew收集器除了采用并行回收的方式执行内存回收外,两款垃圾收集器之间几乎没有任何区别。ParNew收集器在年轻代中同样也是采用复制算法、"Stop一 the一World"机制

③. 因为除Serial外,目前只有ParNew GC(JDK8移除这种组合)能与CMS(JDK14删除CMS)收集器配合工作

④. 在程序中,开发人员可以通过选项"-XX:+UseParNewGC"手动指定使用.ParNew收集器执行内存回收任务。它表示年轻代使用并行收集器, 不影响老年代

⑤. -XX:ParallelGCThreads 限制线程数量,默认开启和CPU数据相同的线程数

⑥. SerialGC 和 ParNewGC哪个更好?

  • ParNew 收集器运行在多CPU的环境下,由于可以充分利用多CPU、多核心等物理硬件资源优势,可以更快速地完成垃圾收集,提升程序的吞吐量
  • 但是在单个CPU的环境下,ParNew收集器不比Serial 收集器更高效。虽然Serial收集器是基于串行回收,但是由于CPU不需要频繁地做任务切换,因此可以有效避免多线程交互过程中产生的一些额外开销


(3). Parallel、ParallelOld:吞吐量优先

在这里插入图片描述

 ①. HotSpot的年轻代中除了拥有ParNew收集器是基于并行回收的以外, Parallel Scavenge收集器同样也采用了复制算法、并行回收和"Stop the World"机制。在Java8中,默认是此垃圾收集器

②. 那么Parallel收集器的出现是否多此一举?

  • 和ParNew收集器不同,Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughp ut),它也被称为吞吐量优先的垃圾收集器
  • 自适应调节策略也是Parallel Scavenge与ParNew一个重要区别

③. 高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。因此,常见在服务器环境中使用。例如,那些执行批量处理、订单处理、工资支付、科学计算的应用程序

④. Parallel收集器在JDK1.6时提供了用于执行老年代垃圾收集的 Parallel 0ld收集器,用来代替老年代的Serial 0ld收集器

⑤. Parallel 0ld收集器采用了标记一压缩算法,但同样也是基于并行回收和”Stop一the一World"机制

⑥. 在程序吞吐量优先的应用场景中,Parallel 收集器和Parallel 0ld收集器的组合,在Server模式下的内存回收性能很不错

⑦. 参数配置

  • -XX:+UseParallelGC手动指定年轻代使用Parallel并行收集器执行内存回收任务
  • -XX:+UseParallelOldGC手动指定老年代都是使用并行回收收集器。

                (分别适用于新生代和老年代。默认jdk8是开启的。上面两个参数,默认开启一个,另一个         也会被开启。(互相激活))

  • -XX:ParallelGCThreads 设置年轻代并行收集器的线程数。一般地,最好与CPU数量相等,以避免过多的线程数影响垃圾收集性能

                (在默认情况下,当CPU 数量小于8个, ParallelGCThreads 的值等于CPU 数量。
                当CPU数量大于8个,ParallelGCThreads 的值等于3+[ 5 * 个数 ]/8] )

  • -XX:MaxGCPauseMillis:设置垃圾收集器最大停顿时间(即STW的时间)。单位是毫秒

                (为了尽可能地把停顿时间控制在MaxGCPauseMills以内,收集器在工作时会调整Java堆          大小或者其他一些参数
                对于用户来讲,停顿时间越短体验越好。但是在服务器端,我们注重高并发,整体的吞吐         量。所以服务器端适合Parallel, 进行控制该参数使用需谨慎)

  • -XX: GCTimeRatio 垃圾收集时间占总时间的比例 ( 1 / N + 1 ), 用于衡量吞吐量的大小

                取值范围(0,100),默认值99,也就是垃圾回收时间不超过1号。
                与前一个-XX :MaxGCPauseMillis参数有一 定矛盾性。暂停时间越
        长,Radio参数就容易超过设定的比例。

  • -XX:+UseAdaptiveSizePolicy 设置Parallel Scavenge收集器具有自适应调节策略

                (在这种模式下,年轻代的大小、Eden和Survivor的比例、晋升老年代的对象年龄等参数会         被自动调整,已达到在堆大小、吞吐量和停顿时间之间的平衡点
                在手动调优比较困难的场合,可以直接使用这种自适应的方式,仅指定虚拟机的最大堆目         标的吞吐量(GCTimeRatio)停顿时间(MaxGCPauseMills),让虚拟机自己完成调优工作)

(4).CSM回收器:低延迟

篇幅较长,另外开了一篇文章: CMS低延迟垃圾收集器详解

(5).G1垃圾收集器

篇幅较长,另外开了一篇文章: G1垃圾收集器详解

(6).7款垃圾回收器总结

在这里插入图片描述

(7).怎么选择垃圾回收器?

Java垃圾收集器的配置对于JVM优化来说是一个很重要的选择,选择合适的
垃圾收集器可以让JVM的性能有一个很大的提升。

1.优先调整堆的大小让JVM自适应完成。
2.如果内存小于100M,使用串行收集器
3.如果是单核、单机程序,并且没有停顿时间的要求,串行收集器
4.如果是多CPU、需要高吞吐量、允许停顿时间超过1秒,选择并行或者JVM自
己选择
5.如果是多CPU、追求低停顿时间,需快速响应(比如延迟不能超过1秒,如互
联网应用),使用并发收集器
6.官方推荐G1,性能高。现在互联网的项目,基本都是使用G1

5.GC日志分析

通过阅读GC日志,我们可以了解Java虚拟机内存分配与回收策略。
内存分配与垃圾回收的参数列表
-XX: +PrintGC     输出GC日志。类似: -verbose :ge
-XX: +PrintGCDetails  输出Gc的详细日志
-XX: +PrintGCTimeStamps  输出Gc的时间戳( 以基准时间的形式)
-XX: +PrintGCDateStamps  输出GC的时间戳(以日期的形式,如2013-05-04T21 :53:59.234+0800)
-XX: +PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径

  • -verbose:gc

  • -verbose:gc  -XX: +PrintGCDetails

 GC日志分析工具

  • 常用的日志分析工具有: GCViewer. GCEasy、 GCHisto、 GCLogViewer、Hpjmeter、garbagecat等。

6.其他的GC

①革命性的ZGC

ZGC在尽可能对吞吐量影响不大的前提下,实现在任意堆内存大小下都可以把垃圾收集的停顿时间限制在十毫秒以内的低延迟。

《深入理解Java虚拟机》一书中这样定义ZGC:

  • ZGC收集器是一款基于Region内存布局的,( 暂时)不设分代的,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-压缩算法的,以低延迟为首要目标的一款垃圾收集器。
  • ZGC的工作过程可以分为4个阶段:并发标记-并发预备重分配-并发重分配-并发重映射等。
  • ZGC几乎在所有地方并发执行的,除了初始标记的是STW的。所以停顿时间.几乎就耗费在初始标记上,这部分的实际时间是非常少的。

虽然ZGC还在试验状态,没有完成所有特性,但此时性能已经相当亮眼,用“令人震惊、革命性”来形容,不为过。

未来将在服务端、大内存、低延迟应用的首选垃圾收集器。

ZGC的应用

  • JDK14之前,ZGC仅Linux才支持。
  • 尽管许多使用ZGC的用户都使用类Linux的环境,但在Windows 和macOS.上,人们也需要ZGc进行开发部署和测试。许多桌面应用也可以从ZGC中受益。因此,ZGC特性 被移植到了Windows和macos上。
  • 现在mac或Windows.上也能使用zGc了,示例如下:-XX: +UnlockExperimentalVMOptions -XX : +UseZGC

②.AliGC

   

下一篇 13 性能监控与调优篇

参考视频 : 尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机)
参考书籍 : 深入理解Java虚拟机
 

猜你喜欢

转载自blog.csdn.net/qq_43295483/article/details/120215532