有 8 种不同的垃圾回收器,它们分别用于不同分代的垃圾清理
下图是各种垃圾回收器之间的关系,连线表示相互可以配合使用。
收集器 | 特点 | 介绍 |
Serial | 新生代收集器 串行 标记-复制 |
在回收时,JVM会暂停所有用户线程,执行垃圾回收操作,直到操作完成后再继续用户线程。由于该垃圾回收器只使用一个线程,因此也被称为串行垃圾回收器。 |
Serial Old | 老年代 串行 标记-整理 |
是Serial收集器的升级版,用于回收老年代内存。该垃圾回收器也只使用一个线程进行回收。 |
ParNew | 新生代 多线程并行执行 “标记-复制”算法 |
是一种并行垃圾回收器,主要用于新生代内存的回收。该垃圾回收器使用多线程并行执行“标记-复制”算法。 |
Parallel Scavenge | 新生代 多线程并行执行 “标记-复制”算法 |
是一种并行垃圾回收器,主要用于新生代内存的回收。它优化了垃圾回收的吞吐量,适用于那些重视系统吞吐量的应用场景。该垃圾回收器使用多线程并行执行“标记-复制”算法,并且可以动态调整回收时间,以适应不同的内存负载。 |
Parallel Old | 使用多线程并行执行“标记-整理”算法 老年代 |
是一种并行垃圾回收器,用于回收老年代内存。该垃圾回收器使用多线程并行执行“标记-整理”算法。 |
CMS Concurrent Mark Sweep) |
使用多线程并行执行,低延时,减少STW对用户的影响 “标记-清除”算法 老年代 |
是一种混合垃圾回收器,主要用于回收老年代内存。该垃圾回收器使用多线程并行执行“标记-清除”算法,其中标记阶段是并发执行的,即在不停顿用户线程的情况下标记可回收对象,而清除阶段则会暂停用户线程。首次实现了让垃圾收集线程与用户线程(基本上)同时工作(并发收集) |
G1 Garbage-First |
使用多线程并行执行“标记-整理”算法 吞吐量和低延时都行的整堆垃圾收集器 可预测停顿 |
是一个横跨新生代和老年代的垃圾回收器。它打乱了以前的堆结构,直接将堆分成极其多个区域。每个区域都可以充当 Eden 区、Survivor 区或者老年代中的一个。它采用的是标记 - 压缩算法,而且和 CMS 一样都能够在应用程序运行过程中并发地进行垃圾回收。G1 能够针对每个细分的区域来进行垃圾回收。在选择进行垃圾回收的区域时,它会优先回收死亡对象较多的区域。这也是 G1 名字的由来。 |
zgc Z Garbage Collector |
使用多线程并行执行“标记-复制”算法 整堆 |
是一种可伸缩低延迟的垃圾回收器,在大多数情况下,垃圾收集的停顿时间不到10毫秒。与CMS中的ParNew和G1类似,ZGC也采用标记-复制算法,不过ZGC对该算法做了重大改进:ZGC在标记、转移和重定位阶段几乎都是并发的,这是ZGC实现停顿时间小于10ms目标的最关键原因。ZGC的设计思想是将Java堆分成一些小块,每个块都可以独立地进行垃圾回收。这样,在垃圾收集过程中,只需要回收一小部分的Java堆,从而实现短暂的停顿时间。同时,ZGC还支持动态地调整Java堆的大小,以便更好地适应应用程序的需求。 |
单线程收集器,发展历史最悠久的收集器,当它在进行垃圾收集工作的时候,其他线程都必须暂停直到垃圾收集结束(Stop The World)。
虽然Serial收集器存在Stop The World的问题,但是在并行能力较弱的单CPU环境下往往表现优于其他收集器;因为它简单而高效,没有多余的线程交互开销;Serial对于运行在Client模式下的虚拟机来说是个很好的选择
使用-XX:+UseSerialGC
参数可以设置这个Serial收集器
特点:
Parallel Scavenge
收集器是采用复制算法的多线程新生代收集器,它与其他的收集器的不同之处在于它主要关心的是吞吐量,而其他的收集器关注的是尽可能的减少用户线程的等待时间(缩短Stop The World
的时间)。
吞吐量=用户线程执行时间/(用户线程执行时间+垃圾收集时间),虚拟机总共运行100分钟,其中垃圾收集花费时间1分钟,那么吞吐量就是 99%
停顿时间越短适合需要和用户进行交互的程序,良好的响应能够提升用户的体验。而高效的吞吐量可以充分的利用CPU时间,尽快的完成计算任务,所以Parallel Scavenge收集器适用于后台计算型任务程序。
-XX:+UseParallelGC
设置使用该回收器
特点:
Parallel Scavenge
,采用复制算法Serial Old
,采用标记-整理算法Parallel Old收集器可以配合Parallel Scavenge收集器一起使用达到“吞吐量优先”,它主要是针对老年代的收集器,使用的是标记-整理算法。在注重吞吐量的任务中可以优先考虑使用这个组合。自 JDK 9 开始,Parallel Old
垃圾回收器已被 G1(Garbage-First)
垃圾回收器取代为默认老年代回收器。
适用于吞吐量优先的应用场景,例如批处理任务、数据处理任务等。
-XX:+UseParallelOldGc
设置老年代使用该回收器。
XX:+ParallelGCThreads
设置垃圾收集时的线程数量。
特点:
Parallel Old
,采用标记-整理算法Parallel Scavenge
,采用复制算法ParNew收集器是Serial收集器的多线程版本;除了使用了多线程进行垃圾收集以外,其他的都和Serial一致;它默认开始的线程数与CPU的核数相同,可以通过参数-XX:ParallelGCThreads
来设置线程数。
从最上面的图可以看出,能够与CMS配合使用的收集器,除了Serial以外,就只剩下ParNew,所以ParNew通常是运行在Server模式下的首选新生代垃圾收集器
使用·-XX:+UseParNewGC·参数可以设置新生代使用这个并行回收器
特点:
ParNew
,Serial的多线程版。SerialOld
CMS收集器是一种以获取最短回收停顿时间为目标的收集器,在互联网网站、B/S架构的中常用的收集器就是CMS,因为系统停顿的时间最短,给用户带来较好的体验。JDK 9 及以后的版本中已被标记为废弃(deprecated),JDK 14被移除。
适用于响应时间优先的应用场景,例如 Web 服务器、实时系统等,它们对于长时间的停顿非常敏感。
-XX:+UseConcMarkSweepGC
设置老年代使用该回收器。
-XX:ConcGCThreads
设置并发线程数量。
新生代默认ParNew
老年代:CMS
特点:
-XX:CMSInitiatingoccupancyFraction
来设置;如果回收阀值设置的太大,在CMS运行期间如果分配大的对象找不到足够的空间就会出现“Concurrent Mode Failure”失败,这时候会临时启动SerialOld GC来重新进行老年代的收集,这样的话停顿的时间就会加长。-XX:+UseCMSCompactAtFullCollecion
,如果启用,在Full GC的时候开启内存碎片整理合并过程,由于内存碎片整理的过程无法并行执行,所以停顿的时间会加长。考虑到每次FullGC都要进行内存碎片合并不是很合适,所以CMS又提供了另一个参数 -XX:CMSFullGCsBeforeCompaction
来控制执行多少次不带碎片整理的FullGC之后,来一次带碎片整理GCG1是一款面向服务端应用的全功能型垃圾收集器,大内存企业配置的主要是G1。
G1 算法取消了堆中年轻代与老年代的物理划分,但它仍然属于分代收集器。G1 算法将堆划分为若干个区域,称作 Region,如下图中的小方格所示。一部分区域用作年轻代,一部分用作老年代,另外还有一种专门用来存储巨型对象的分区。
第31页-120.PNG
-XX:+UseG1GC
image.png
G1 也和 CMS 一样会遍历全部的对象,然后标记对象引用情况,在清除对象后会对区域进行复制移动整合碎片空间。
G1 采用每次只清理一部分而不是全部的 Region 的增量式清理,由此来保证每次 GC 停顿时间不会过长。
标记阶段停顿分析
清理阶段停顿分析
复制阶段停顿分析
ZGC ( Z Garbage Collector )在 JDK11中引入的一种可扩展的低延迟垃圾收集器,在 JDK15中发布稳定版。
ZGC 针对大堆内存设计可以支持 TB 级别的堆,ZGC 非常高效,能够做到 10ms 以下的回收停顿时间,JDK16发布后,GC停顿时间已经缩小到1ms以内。
-XX:+UseZGC #启用ZGC
-Xmx # 设置最大堆内存
-xlog:gc # 打印 GC日志
-Xlog:gc* # 打印 GC 详细日志
特点:zgc 内存分布
跟 G1 类似,ZGC 的堆内存也是基于 Region 来分布,不过 ZGC 是不区分新生代老年代的。不同的是,ZGC 的 Region 支持动态地创建和销毁,并且 Region 的大小不是固定的,包括三种类型的 Region
image.png
这么快的响应,ZGC 是如何做到的呢?这是由于 ZGC 具有以下特点。
读屏障是JVM向应用代码插入一小段代码的技术。当应用线程从堆中读取对象引用时,就会执行这段代码。需要注意的是,仅“从堆中读取对象引用”才会触发这段代码。
image.png