Java中常用的垃圾回收器 JVM中常见的几种垃圾回收器(Parallel、CMS、G1)
垃圾回收(Garbage Collection, GC)是Java虚拟机(JVM)的一个重要部分,它帮助开发者管理内存,确保程序能有效并且安全地运行。本文将介绍Java中几种常用的垃圾回收器,以及它们的工作原理和适用场景。
1. 什么是垃圾回收?
在Java中,对象是在堆内存上分配的,当这些对象不再被任何部分的应用所引用时,这些对象就被认为是垃圾。垃圾回收器的任务就是找到这些不再被需要的对象,并释放它们占据的内存空间,以供新对象使用。
2. 为什么需要垃圾回收?
无法控制内存是许多程序可能遇到的问题。例如,内存泄漏,这通常发生在程序不再需要的数据没有被及时清理的情况下,长此以往可能导致内存耗尽,最终程序崩溃。通过自动垃圾回收,Java试图避免这种情况。
3. 垃圾回收器的种类
3.1 Serial GC
Serial GC是最基本的垃圾回收器,适用于单核处理器的环境。它使用到的是单线程进行垃圾回收
,它在进行垃圾回收时会暂停所有的应用线程
("Stop-The-World"事件),这通常不适用于多线程的服务器环境,但可能适合于简单的命令行程序。特点:单线程、STW、复制和标记整理算法
使用参数:
-XX:+UseSerialGC
3.2 Parallel GC
Parallel GC它使用多个线程
来执行垃圾回收,因此它比Serial GC更适合多核心服务器环境。其主要关注点是提高吞吐量
,缺点是在垃圾回收时仍然会有应用线程暂停STW
。在JDK 4中引入,在JDK 8中是默认垃圾回收器。特点:并发、STW、复制和标记整理算法
使用参数:
-XX:+UseParallelGC
3.3 CMS GC
CMS GC 最短停顿时间
为目标的垃圾回收器。它在回收内存的同时允许应用程序线程继续执行,适合于交互性强的应用,其中快速响应比吞吐量更重要。在JDK5中引入。特点:并发、较短STW、标记清除算法有内存碎片
使用参数:
-XX:+UseConcMarkSweepGC
初始标记
:GC为单线程,暂停所有用户线程,只标记GC Root 直接引用的对象并发标记
:多个GC线程和用户线程同时工作,从GC Roots开始遍历整个对象图,找出所有可达对象。
使用三色标记法,对象被分为三种颜色:白色(未被扫描)、灰色(已扫描但其引用尚未全部扫描)、黑色(已扫描且其引用也已全部扫描)。初始时,所有对象被视为白色。随着扫描的进行,对象从白色变为灰色,然后变为黑色。所有可达对象标记为黑色。重新标记
:由于并发标记阶段用户程序仍在运行,可能会导致部分对象的标记状态发生改变,重新标记阶段就是为了修正这些变动。
多个GC线程STW进行。并发清理
:多个GC线程并发清理那些在标记阶段被标记为垃圾(即未被标记为黑色的对象)的对象,释放内存空间。并发重置
:多个GC线程并发将存活对象上的标记给移除掉,避免影等下次gc
3.4 G1 GC
G1 GC (Garbage-First)是一种服务器端垃圾回收器,它旨在填补Parallel GC和CMS GC之间的空白,提供一个低延迟的垃圾回收解决方案
。G1将堆内存分成多个不同的区域,并根据每个区域可能包含垃圾的大小来确定回收的优先顺序。在JDK 7 update 4中引入,JDK 9成为默认的垃圾回收器。特点:最短STW、并发、空间整理
使用参数:
-XX:+UseG1GC
初始标记
:GC单线程进行标记,暂停所有用户线程,记录GC ROOT 直接引用的对象并发标记
:多个GC线程和用户线程同时工作,从GC Roots开始遍历整个对象图,找出所有可达对象。
使用三色标记法,对象被分为三种颜色:白色(未被扫描)、灰色(已扫描但其引用尚未全部扫描)、黑色(已扫描且其引用也已全部扫描)。初始时,所有对象被视为白色。随着扫描的进行,对象从白色变为灰色,然后变为黑色。所有可达对象标记为黑色。最终标记
:因为上个过程是并发进行的,所以有些对象是在标记过程中新产生的,导致没有标记上,所以这个过程进行修正,将这些漏网之鱼都标上,这个过程是多线程 STW 进行。可以通过增量标记或使用写屏障技术来减少需要重新标记的对象数量。筛选回收
:计算每个region的回收成本,并按照回收成本进行排序,然后按照用户期望的停顿时间进行计算,决定应该回收哪些region。可以通过更智能的region选择算法来优化,例如优先回收垃圾对象最多的region或回收成本最低的region。
3.5 Z GC
ZGC是一种低延迟的垃圾回收器,目前仍在积极开发中。ZGC的设计初衷是为了在大堆内存上工作,并且几乎不产生延迟。这使得它非常适合需要快速响应但是内存占用大的应用程序。
使用参数:
-XX:+UseZGC
3.6 Shenandoah GC
Shenandoah GC与ZGC有类似的目标:减少停顿时间,即便是在大堆或者多核心处理器的情况下。Shenandoah通过在GC的许多阶段与应用线程并发执行来实现这一目标。
使用参数:
-XX:+UseShenandoahGC
4. 怎样选择合适的垃圾回收器?
在选择垃圾回收器时,需要考虑应用程序的需求:
- 对于需要最大吞吐量的应用程序,Parallel GC可能是最好的选择。
- 如果应用需要较短的响应时间或者较少的停顿时间,可以考虑CMS或G1。
- 对于需要极低停顿时间并且具有大内存容量的应用,ZGC或者Shenandoah可能是合适的选择。
- 在实际中,最好的方法是通过实际的性能测试来确定最适合你的应用的垃圾回收器。
5. JDK 8 默认垃圾回收器 Parallel
JDK 8(Java Development Kit 8)中默认的垃圾回收器组合为Parallel Scavenge(用于Young Generation)加上Parallel Old(用于Old Generation)
。Parallel GC 在 JDK 5 中开始成为默认的垃圾回收器,特点:并行+STW
。
Parallel Scavenge收集器是一个新生代垃圾收集器,它使用复制算法
,而且是并行的多线程收集器
。它主要关注于达到一个可控制的吞吐量(Throughput)。所谓吞吐量,就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即:吞吐量 = 用户代码时间 /(用户代码时间 + 垃圾收集时间)。服务器环境中,这种收集器能够最大化的利用CPU时间,尽快地完成程序的运算任务,特别适用于多核服务器。
Parallel Old收集器是Parallel Scavenge收集器的老年代版本
,使用多线程和"标记-整理"
算法。这个收集器是在JDK 6u14版之后引入的。从JDK 9 开始默认垃圾回收器是 G1 (Garbage-First)
6.为何CMS不能作为默认垃圾回收器
CMS(Concurrent Mark-Sweep)收集器是一个以获取最短回收停顿时间为目标的收集器
,使用"标记-清除
"算法,并且是并发的。但是在JDK版本的迭代中没有任何一个版本使用CMS为一个默认的垃圾回收器。
选择Parallel Scavenge和Parallel Old的原因而不用CMS,通常基于以下考虑:
吞吐量优先: 如果应用不是非常注重服务响应时间,而是更希望在单位时间内完成更多的工作,即追求较高的吞吐量,那么Parallel Scavenge加上Parallel Old垃圾收集器会是一个更合适的选择。
碎片化问题:CMS使用的"标记-清除"算法容易导致内存碎片化,当碎片化严重到一定程度,CMS需要进行一次完全停顿的垃圾收集以整理内存碎片(Full GC),这可能导致较长时间的停顿。而Parallel Old使用的"标记-整理"算法可以有效避免内存碎片化问题。
更简单稳定: 相较于CMS,Parallel Scavenge和Parallel Old通常来说维护起来比较简单,参数配置也更容易。而CMS有几百个参数需要配置调整,对于维护的成本是非常高的。
随着Java 9的发布,G1(Garbage-First)垃圾收集器成为了默认的垃圾收集器,它旨在替代CMS,提供更好的性能表现和更简单的调优选项。而在Java 11之后,还引入了ZGC和Shenandoah等实验性的低停顿时间垃圾收集器。
7. CMS和G1比较
CMS(Concurrent Mark Sweep)和G1(Garbage-First)是Java虚拟机中的两种不同的垃圾回收器,它们各自有不同的特点和适用场景。
7. 1 CMS垃圾回收流程
初始标记
:GC为单线程,暂停所有用户线程,只标记GC Root 直接引用的对象并发标记
:多个GC线程和用户线程同时工作,从GC Roots开始遍历整个对象图,找出所有可达对象。
使用三色标记法,对象被分为三种颜色:白色(未被扫描)、灰色(已扫描但其引用尚未全部扫描)、黑色(已扫描且其引用也已全部扫描)。初始时,所有对象被视为白色。随着扫描的进行,对象从白色变为灰色,然后变为黑色。所有可达对象标记为黑色。重新标记
:由于并发标记阶段用户程序仍在运行,可能会导致部分对象的标记状态发生改变,重新标记阶段就是为了修正这些变动。
多个GC线程STW进行。并发清理
:多个GC线程并发清理那些在标记阶段被标记为垃圾(即未被标记为黑色的对象)的对象,释放内存空间。并发重置
:多个GC线程并发将存活对象上的标记给移除掉,避免影等下次gc
7. 2 G1垃圾回收流程
初始标记
:GC单线程进行标记,暂停所有用户线程,记录GC ROOT 直接引用的对象并发标记
:多个GC线程和用户线程同时工作,从GC Roots开始遍历整个对象图,找出所有可达对象。
使用三色标记法,对象被分为三种颜色:白色(未被扫描)、灰色(已扫描但其引用尚未全部扫描)、黑色(已扫描且其引用也已全部扫描)。初始时,所有对象被视为白色。随着扫描的进行,对象从白色变为灰色,然后变为黑色。所有可达对象标记为黑色。最终标记
:因为上个过程是并发进行的,所以有些对象是在标记过程中新产生的,导致没有标记上,所以这个过程进行修正,将这些漏网之鱼都标上,这个过程是多线程 STW 进行。可以通过增量标记或使用写屏障技术来减少需要重新标记的对象数量。筛选回收
:计算每个region的回收成本,并按照回收成本进行排序,然后按照用户期望的停顿时间进行计算,决定应该回收哪些region。可以通过更智能的region选择算法来优化,例如优先回收垃圾对象最多的region或回收成本最低的region。
G1收集器是为了替代CMS收集器而设计的,提供更可预测的垃圾回收暂停时间,特别是在处理大内存容量的多处理器机器时。G1能够更好地控制停顿时间,通过将堆分割成多个区域(Region)来避免整个堆的垃圾回收,逐步地清理内存,特别适合大堆内存的应用。
7. 3 两者比较
- 停顿时间控制:G1具有更好的停顿时间控制能力,可以为应用程序设置停顿时间目标。
- 内存占用:G1通常比CMS需要更多的内存空间,因为它需要维护更多的内存结构。
- 预测性能:G1可以更准确地预测垃圾收集的停顿时间,因为它是基于区域的回收。
- 适用场景:CMS适用于对响应时间比较敏感的应用。而G1适用于堆内存较大的场景,特别是超过4GB的堆内存。
随着Java虚拟机的发展,G1由于其更好的可预测性和适应性,越来越多地成为默认的垃圾收集器,尤其是在Java 9及以后的版本中。