阅读 35

JVM--JVM经典垃圾收集器整理(Serial收集器、ParNew收集器、Parallel Scavenge收集器、Garbage First收集器、ZGC)

echo编辑整理。欢迎添加echo微信(微信号:t2421499075)交流学习。该文章不支持转载,主要内容来自读书笔记和网络博客,以及视频学习整理。


参考资料列表:

  • 1、深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)
  • 2、Serial收集器和ParNew收集器:blog.csdn.net/wxy941011/a…

注:本文说的垃圾收集器基本都是JDK1.7以后的垃圾收集器,不讨论JDK1.7以前的。

经典垃圾收集器盘点

  • 1、Serial收集器
  • 2、ParNew收集器
  • 3、Parallel Scavenge收集器
  • 4、Serial Old收集器
  • 5、Parallel Old收集器
  • 6、CMS收集器
  • 7、Garbage First收集器
  • 8、Shenandoah收集器
  • 9、ZGC收集器
  • 10、Epsilon收集器

Serial收集器

Serial收集器是最基础、历史最悠久的收集器,曾经(在JDK1.3.1之前)是HotSpot虚拟机新生代收集器的唯一选择,时至今日,垃圾收集器的不断改进,不断出新,Serial依然在我们的垃圾收集器的选项里面。

Serial收集器运行示意图

在这里插入图片描述

Serial收集器的特点:

  • 优点:简单高效,拥有很高的单线程收集效率
  • 缺点:收集过程需要暂停所有线程
  • 使用算法:复制算法
  • 适用范围:新生代
  • 应用:Client模式下的默认新生代收集器

对于Serial收集器缺点有一个比较有意思的故事:Serial收集器的运行过程需要暂停应用程序所有线程(Stop The World),它带给用户的恶劣体验。早期HotSpot虚拟机的设计者们表示完全理解,但也同时表示非常委屈:“你妈妈在给你打扫房间的时候,肯定也会让你老老实实地在椅子上或者房间外待着,如果她一边打扫,你一边乱扔纸屑,这房间还能打扫完?”这确实是一个合情合理的矛盾,虽然垃圾收集这项工作听起来和打扫房间属于一个工种,但实际上肯定还要比打扫房间复杂得多!(现在的垃圾收集器一样需要停顿,只是都做到了毫秒级)

使用该垃圾收集器 -XX:+UseSerialGC

使用之前我们可以去查看一下,当前虚拟机是否使用的Serial,如果是我们不需要去更改,如果不是则添加参数:-XX:+UseSerialGC。更改完成之后,我们输出一下jvm参数,可以看到如下图输出。

在这里插入图片描述

ParNew收集器

ParNew收集器实质上是Serial收集器的多线程并行版本

ParNew收集器运行示意图

在这里插入图片描述

ParNew收集器的特点:

  • 优点:在多CPU时,比Serial效率高。
  • 缺点:收集过程暂停所有应用程序线程,单CPU时比Serial效率差。
  • 使用算法:复制算法
  • 适用范围:新生代
  • 应用:运行在Server模式下的虚拟机中首选的新生代收集器

使用ParNew收集器 -XX:+UseParNewGC

在这里插入图片描述

Parallel Scavenge收集器

Parallel Scavenge收集器也是一款新生代收集器,它同样是基于标记-复制算法实现的收集器,也是能够并行收集的多线程收集器……Parallel Scavenge的诸多特性从表面上看和ParNew非常相似,Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值。

  • 吞吐量 = 运行用户代码时间/运行用户代码时间 + 运行垃圾收集器时间。
  • 比如虚拟机总共运行了100分钟,垃圾收集时间用了1分钟,吞吐量=(100-1)/100=99%。
  • 若吞吐量越大,意味着垃圾收集的时间越短,则用户代码可以充分利用CPU资源,尽快完成程序的运算任务。

吞吐量我们可以自行控制?

吞吐量我们从上面的描述可以看到,他是一个比值。那么我们自然能够通过调整比值的参数来影响吞吐量。主要涉及的命令有一下两个

  • -XX:MaxGCPauseMillis 控制最大的垃圾收集时间
  • -XX:GCTimeRatio 直接设置吞吐量的大小

根据吞吐量对应的计算公式我们可以看到,当我们的垃圾收集停顿时间变短的时候(垃圾收集停顿是一个大于0的毫秒数),我们的吞吐量在增大。不过大家不要异想天开地认为如果把这个参数的值设置得更小一点就能使得系统的垃圾收集速度变得更快,当我们把这个参数设置的更小的时候,它对应的能够有效收集的时间或者空间会变小。假若停顿时间为100ms能够收集500M的堆空间,那么50ms能够收集的空间可能会低于250M的堆空间。垃圾收集器的有效工作时间变短,收集垃圾的效率并不一定提高,同时对应的也只能相对的调整我们的吞吐量。

使用Parallel Scavenge收集器 -XX:+UseParallelGC

在这里插入图片描述

Serial Old收集器

Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。这个收集器的主要意义也是供客户端模式下的HotSpot虚拟机使用。

Serial Old收集器运行示意图

在这里插入图片描述

Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和"标记-整理算法"进行垃圾回收。

CMS收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为四个步骤,包括:

  • 1)初始标记(CMS initial mark)
  • 2)并发标记(CMS concurrent mark)
  • 3)重新标记(CMS remark)
  • 4)并发清除(CMS concurrent sweep)

由于整个过程中,并发标记和并发清除,收集器线程可以与用户线程一起工作,所以总体上来 说,CMS收集器的内存回收过程是与用户线程一起并发地执行的。

CMS收集器特点:

  • 优点:并发收集、低停顿
  • 缺点:产生大量空间碎片、并发阶段会降低吞吐量

CMS收集器运行示意图

在这里插入图片描述

使用CMS收集器 -XX:+UseConcMarkSweepGC

Garbage First收集器

Garbage First(简称G1)收集器是垃圾收集器技术发展历史上的里程碑式的成果,它是一款主要面向服务端应用的垃圾收集器

使用G1收集器时,Java堆的内存布局与就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。

G1收集器的运作过程大致可划分为以下四个步骤:

  • 初始标记(Initial Marking)
  • 并发标记(Concurrent Marking)
  • 最终标记(Final Marking)
  • 筛选回收(Live Data Counting and Evacuation)

判断是否需要使用G1收集器?

  • (1)50%以上的堆被存活对象占用
  • (2)对象分配和晋升的速度变化非常大
  • (3)垃圾回收时间比较长

使用Garbage First收集器 -XX:+UseG1G