7种垃圾回收器特点,优劣及使用场景
7种垃圾回收器特点,优劣及使用场景
一、常见垃圾收集器
==++++常见的垃圾回收器种类++++==
- 新生代收集器
- Serial
- ParNew
- parallel
- 老年代收集器
- Serial Old
- CMS
- Parallel Old
- 新生代和老年代收集器
- G1
每种垃圾回收器之间不是独立操作的,下图表示垃圾回收器之间有连线表示,可以协作使用。
二、新生代垃圾收集器
==1、Serial收集器==
- 概述:Serial是一类用于新生代的单线程收集器,采用++复制算法++进行垃圾收集。Serial进行垃圾收集时,不仅只用一条单线程执行垃圾收集工作,它还在收集的同时,所用的用户必须暂停。其执行过程如下图所示
serial垃圾收集器执行过程
从上图可知当应用程序进行到一个安全的节点的时候,所有的线程全都暂停,等到GC完成后,应用程序线程继续执行。这就像是你一边扫地,旁边要是有人一边嗑瓜子,那你这要一直扫下去的节奏,只能先让他别吃了,然后你才能干活。
- 优势:简单高效,由于采用的是单线程的方法,因此与其他类型的收集器相比,对单个cpu来说没有了上下文之间的的切换,效率比较高。
- 缺点:会在用户不知道的情况下停止所有工作线程,用户体验感极差,令人难以接受。
- 适用场景:Client 模式(桌面应用);单核服务器。
- 参数: 可以使用命令如下开启Serial作为新生代收集器
-XX:+UserSerialGC #选择Serial作为新生代垃圾收集器
==2、ParNew收集器==
- 概述:parNew收集器其实就是Serial的一个多线程版本,其在单核cpu上的表现并不会比Serail收集器更好,在多核机器上,其默认开启的收集线程数与cpu数量相等。可以通过如下命令进行修改
-XX:ParallelGCThreads #设置JVM垃圾收集的线程数
如下是ParNew收集器和Serial Old 收集器结合进行垃圾收集的示意图.
ParNew与serialOld当用户线程都执行到++安全点++时,所有线程暂停执行,采用复制算法进行垃圾收集工作,完成之后,用户线程继续开始执行。
- 优点:随着cpu的有效利用,对于GC时系统资源的有效利用有好处。
- 缺点:和Serial是一样的。
- 适用场景:ParNew是许多运行在Server模式下的虚拟机中首选的新生代收集器。因为CMS收集器只能与serial或者parNew联合使用,在当下多核系统环境下,首选的是parNew与CMS配合。ParNew收集器也是使用CMS收集器后默认的新生代收集器。也可以使用如下命令进行强制指定。
-XX:UseParNewGC #新生代采用ParNew收集器
==3、Parallel Scavenge收集器==
-
概述:Parallel Scavenge也是一款用于新生代的多线程收集器,也是采用复制算法。++与ParNew的不同之处在于 Parallel Scavenge收集器的目的是达到一个可控制的吞吐量,而ParNew收集器关注点在于尽可能的缩短垃圾收集时用户线程的停顿时间。++ 所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值, 即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。
Parallel.
例如虚拟机一共运行了 100 分钟,其中垃圾收集花费了 1 分钟,那吞吐量就是 99% 。比如下面两个场景,垃圾收集器每 100 秒收集一次,每次停顿 10 秒,和垃圾收集器每 50 秒收集一次,每次停顿时间 7 秒,虽然后者每次停顿时间变短了,但是总体吞吐量变低了,CPU 总体利用率变低了。其与Parallel Old收集器运行示意图如下
-
优点: 追求高吞吐量,高效利用CPU,是吞吐量优先,且能进行精确控制。
-
缺点: ""
-
适用场景:注重吞吐量高效利用CPU,需要高效运算,且不需要太多交互。
-
参数:
- ==-XX:MaxGCPauseMilis==。 控制最大垃圾收集停顿时间,参数值是一个大于0的毫秒数,收集器尽可能保证回收花费时间不超过设定值。但将这个值调小,并不一定会使系统垃圾回收速度更快,GC停顿时间是以牺牲吞吐量和新生代空间换来的。
- ==-XX:GCTimeRadio==。设置吞吐量大小,参数值是一个(0,100)两侧均为开区间的整数。也是垃圾收集时间占总时间的比率,相当于是吞吐量的倒数。若把参数设置为19,则允许的最大GC时间就占总时间的5%(1/(1+19))。默认值是99,即允许最大1%的垃圾收集时间。
- ==-XX:+UserAdaptiveSizePolicy==。这是一个开关函数,当打开这个函数,就不需要手动指定新生代的大小,Eden与Survivor区的比例(-XX:SurvivorRatio,默认是8:1:1),晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等参数。JVM会动态调整这些参数,以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为GC自适应的调节策略.
三、老年代垃圾收集器
1、Serial Old 收集器
-
概念:Serial Old是Serial收集器的老年代版本,同样是一个单线程收集器,使用标记-整理算法。下图是Serial收集器与Serial Old收集器的运行示意图。
Serial/Serial Old收集器运行示意图 -
适用场景:Client模式;单核服务器;与Parallel Scavenge收集器搭配;作为CMS收集器的后备方案,在并发收集发生Concurrent Mode Failure时使用
2、Parallel Old收集器
-
概念:Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法,可以充分利用多核CPU的计算能力。下图是两种收集器合作的运行示意图
Parallel Scavenge/Parallel Old 收集器运行示意图 -
适用场景:注重吞吐量与CPU资源敏感的场合,与Parallel Scavenge 收集器搭配使用,jdk7和jdk8默认使用该收集器作为老年代收集器。使用参数进行指定
-XX:+UserParallelOldGC
3、CMS收集器
-
概念:CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。采用的算法是“标记-清除”,运作过程分为四个步骤
- 初始标记,标记GC Roots 能够直接关联到达对象
- 并发标记,进行GC Roots Tracing 的过程
- 重新标记,修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录
-
并发清除,用标记清除算法清除对象。
其中初始标记和重新标记这两个步骤仍然需要"stop the world"。耗时最长的并发标记与并发清除过程收集器线程都可以与用户线程一起工作,总体上来说CMS收集器的内存回收过程是与用户线程一起并发执行的。下图是CMS运行示意图。
CMS收集器运行示意图
-
优点:并发收集,低停顿
-
缺点:
- CMS收集器对CPU资源非常敏感,CMS默认启动对回收线程数(CPU数量+3)/4,当CPU数量在4个以上时,并发回收时垃圾收集线程不少于25%,并随着CPU数量的增加而下降,但当CPU数量不足4个时,对用户影响较大。
- CMS无法处理浮动垃圾,可能会出现“Concurrent Mode Failure”失败而导致一次FullGC的产生。这时会地洞后备预案,临时用SerialOld来重新进行老年代的垃圾收集。由于CMS并发清理阶段用户线程还在运行,伴随程序运行自然还会有新的垃圾产生,这部分垃圾出现在标记过程之后,CMS无法在当次处理掉,只能等到下一次GC,这部分垃圾就是浮动垃圾。同时也由于在垃圾收集阶段用户线程还需要运行,那也就需要预留足够的内存空间给用户线程使用,因此CMS收集器不能像其他老年代几乎完全填满再进行收集。可以通过参数-XX:CMSInitiatingOccupancyFraction修改CMS触发的百分比。
- 因为CMS采用的是标记清除算法,因此垃圾回收后会产生空间碎片。通过参数可以进行优化。
-XX:UserCMSCompactAtFullCollection #开启碎片整理(默认是开的) -XX:CMSFullGCsBeforeCompaction #执行多少次不压缩的Full GC之后,跟着来一次压缩的Full GC
-
适用场景:重视服务器响应速度,要求系统停顿时间最短。可以使用参数-XX:+UserConMarkSweepGC来选择CMS作为老年代回收器。
四、新生代和老年代垃圾收集器
G1收集器
- 概念: G1收集器是一款面向服务端应用的垃圾收集器,目前是JDK9的默认垃圾收集器。与其他收集器相比,G1具有如下特点。
- 并行与并发。G1能充分利用多CPU,多核环境下的硬件优势。
- 分代收集。能够采用不同的方式去处理新创建的对象和已经存活了一段时间的对象,不需要与其他收集器进行合作。
- 空间整合。G1从整体上来看基于“标记-整理”算法实现的收集器,从局部上看是基于复制算法实现的,因此G1运行期间不会产生空间碎片。
- 可预测的停顿。G1能建立可预测的时间停顿模型,能让使用者明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。
G1收集器将这个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但两者之间不是物理隔离的。他们都是一部分Region的集合。下图是Java堆的划分示意图。
G1Java堆分区
每一个方块就是一个区域,每个区域可能是 Eden、Survivor、老年代,每种区域的数量也不一定。JVM 启动时会自动设置每个区域的大小(1M ~ 32M,必须是 2 的次幂),最多可以设置 2048 个区域(即支持的最大堆内存为 32M*2048 = 64G),假如设置 -Xmx8g -Xms8g,则每个区域大小为 8g/2048=4M。
G1收集器可以有计划地避免在整个Java堆全区域的垃圾收集。G1可以跟踪各个Region里面垃圾堆积的价值大小(回收所获得的空间大小及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,收集加载最大的region,这种方式保证了有限时间内可以获取尽可能多高的收集效率。
为了在 GC Roots Tracing 的时候避免扫描全堆,在每个 Region 中,都有一个 Remembered Set 来实时记录该区域内的引用类型数据与其他区域数据的引用关系(在前面的几款分代收集中,新生代、老年代中也有一个 Remembered Set 来实时记录与其他区域的引用关系),在标记时直接参考这些引用关系就可以知道这些对象是否应该被清除,而不用扫描全堆的数据。
下图是G收集器运行示意图。从图中可知G1收集器收集器收集过程有初始标记、并发标记、最终标记、筛选回收,和 CMS 收集器前几步的收集过程很相似:
G1垃圾收集
- 过程:
- 初始标记。标记出GC Roots直接关联的对象,这个阶段速度较快,需要停止用户线程,单线程执行。
- 并发标记。从 GC Root 开始对堆中的对象进行可达新分析,找出存活对象,这个阶段耗时较长,但可以和用户线程并发执行。
- 最终标记。修正在并发标记阶段引用户程序执行而产生变动的标记记录。
- 筛选回收。选回收阶段会对各个 Region 的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来指定回收计划(用最少的时间来回收包含垃圾最多的区域,这就是 Garbage First ,第一时间清理垃圾最多的区块),这里为了提高回收效率,并没有采用和用户线程并发执行的方式,而是停顿用户线程。
- 适用场景:要求尽可能可控 GC 停顿时间;内存占用较大的应用。可以用 -XX:+UseG1GC 使用 G1 收集器,jdk9 默认使用 G1 收集器
五、常见参数列表
参数 | 描述 |
---|---|
UserSerialGC | 采用Serial+ Serial Old的收集器组合进行垃圾回收 |
UserParNewGC | 使用ParNew+Serial Old组合 |
UseConMarkSweepGC | 使用ParNew+CMS+SerialOld组合进行垃圾回收。SerialOld是在Concurrent Mode Failure失败后的后备收集器使用 |
UseParallelGC | 虚拟机运行在Server模式下的默认值。使用Parallel Scavenge +Serial Old的收集器组合进行垃圾回收 (jdk8用的就是这个,但是老年代用的是Parallel Old) |
UseParallelOldGC | 使用Parallel Scavenge + Parallel Old组合进行垃圾回收 |
SurvivorRatio | 新生代中Eden区与Survior区域的容量比值,默认为8,代表Eden:Survior=8:1 |
PretenureSizeThreshold | 直接晋升到老年代的对象的大小 |
MaxTenuringThreshold | 晋升到老年代对象的年龄,超过这个数值进入老年代 |
UseAdaptiveSizePolicy | 动态调整Java堆中各个区域的大小以及进入老年代的年龄 |
HandlePromotionFailure | 是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个Eden和Survivor区的 所有对象存活的极端情况 |
ParallelGCThreads | 并行GC时进行内存回收的线程数 |
GCTimeRatio | GC时间占总时间的比率,默认值99%,即允许1%的GC时间,仅在使用Parallel Scavenge收集器时生效 |
MaxGCPauseMillis | GC的最大停顿时间,仅在使用Parallel Scavenge收集器时生效 |
CMSInitiatingOccupancyFraction | 设置CMS在老年代空间被使用多少后触发垃圾回收,默认是92%,仅在使用CMS收集器时生效 |
UseCMSCompactAtFullCollection | 设置CMS收集器在完成垃圾收集后是否要进行一次内存整理。仅在使用CMS收集器时生效 |
CMSFullGCsBeforeCompaction | 设置CMS收集器进行若干次垃圾收集后再启动一次内存碎片整理。仅在使用CMS时生效 |
PrintGCDetails | 查看程序运行时的GC细节 |