一、垃圾收集器概述
垃圾收集器是垃圾回收算法(标记-清除算法、复制算法、标记-整理算法、火车算法)的具体实现,不同商家、不同版本的JVM所提供的垃圾收集器可能会有很在差别,本文主要介绍HotSpot虚拟机中的垃圾收集器。
1-1、垃圾收集器组合
JDK7/8后,HotSpot虚拟机所有收集器及组合(连线),如下图:
(A)、图中展示了7种不同分代的收集器:
Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;
(B)、而它们所处区域,则表明其是属于新生代收集器还是老年代收集器:
新生代收集器:Serial、ParNew、Parallel Scavenge;
老年代收集器:Serial Old、Parallel Old、CMS;
整堆收集器:G1;
(C)、两个收集器间有连线,表明它们可以搭配使用:
Serial/Serial Old、Serial/CMS、ParNew/Serial Old、ParNew/CMS、Parallel Scavenge/Serial Old、Parallel Scavenge/Parallel Old、G1;
(D)、其中Serial Old作为CMS出现"Concurrent Mode Failure"失败的后备预案(后面介绍);
1-2、并发垃圾收集和并行垃圾收集的区别
(A)、并行(Parallel)
指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态;
如ParNew、Parallel Scavenge、Parallel Old;
(B)、并发(Concurrent)
指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行);
用户程序在继续运行,而垃圾收集程序线程运行于另一个CPU上;
如CMS、G1(也有并行);
1-3、Minor GC和Full GC的区别
(A)、Minor GC
又称新生代GC,指发生在新生代的垃圾收集动作;
因为Java对象大多是朝生夕灭,所以Minor GC非常频繁,一般回收速度也比较快;
(B)、Full GC
又称Major GC或老年代GC,指发生在老年代的GC;
出现Full GC经常会伴随至少一次的Minor GC(不是绝对,Parallel Sacvenge收集器就可以选择设置Major GC策略);
Major GC速度一般比Minor GC慢10倍以上;
下面将介绍这些收集器的特性、基本原理和使用场景,并重点分析CMS和G1这两款相对复杂的收集器;但需要明确一个观点:
没有最好的收集器,更没有万能的收集;
选择的只能是适合具体应用场景的收集器。
2、Serial收集器
Serial(串行)垃圾收集器是最基本、发展历史最悠久的收集器;
JDK1.3.1前是HotSpot新生代收集的唯一选择;
串行收集器,新生代收集器
特点:
会产生停顿
新生代使用复制算法
老年代使用标记整理算法
使用在单核CPU环境中
缺点:
会产生Stop the World
优点:
相对于单CPU环境下简单高效,因为没有CPU分配GC线程的开销
使用Serial收集器配置:
-XX:+UseSerialGC 添加该参数来显式的使用串行垃圾收集器;
Serial/Serial Old组合收集器运行示意图如下:
3、ParNew收集器
ParNew垃圾收集器是Serial收集器的多线程版本。
并行(多线程)收集器,用于新生代,使用复制算法
"-XX:+UseConcMarkSweepGC":指定使用CMS后,会默认使用ParNew作为新生代收集器;
"-XX:+UseParNewGC":强制指定使用ParNew;
"-XX:ParallelGCThreads":指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同;
特点:
除了多线程外,其余的行为、特点和Serial收集器一样;
需要多核CPU支持
新生代使用复制算法,老年代使用标记整理算法(串行)
在Server模式下,ParNew收集器是一个非常重要的收集器,因为除Serial外,目前只有它能与CMS收集器配合工作;
CMS是HotSpot在JDK1.5推出的第一款真正意义上的并发(Concurrent)收集器,第一次实现了让垃圾收集线程与用户线程(基本上)同时工作;CMS作为老年代收集器,但却无法与JDK1.4已经存在的新生代收集器Parallel Scavenge配合工作; 因为Parallel Scavenge(以及G1)都没有使用传统的GC收集器代码框架,而另外独立实现;而其余几种收集器则共用了部分的框架代码;
4、Parallel Scavenge收集器
Parallel Scavenge垃圾收集器因为与吞吐量关系密切,也称为吞吐量收集器(Throughput Collector)
新生代收集器,多线程收集器,使用复制算法
主要关注吞吐量,能够最高效的利用CPU
吞吐量=运行用户代码的时间/(运行用户代码的时间+垃圾回收时间)
主要适用于当应用程序运行在具有多个CPU上,对暂停时间没有特别高的要求时,即程序主要在后台进行计算,而不需要与用户进行太多交互;例如,那些执行批量处理、订单处理、工资支付、科学计算的应用程序;
-XX:+UseParallelGC
使用Parallel收集器 + 老年代(串行)
-XX:+UseParallelOldGC
使用Parallel收集器 + 老年代(并行)
-XX:MaxGCPauseMills
最大的停顿时间,单位是毫秒
仅使用与Parallel Scavenge收集器
-XX:GCTimeRatio
垃圾收集时间占总时间的表
0-100取值范围
默认99,也就是说最大容许1%的时间做GC
仅使用与Parallel Scavenge收集器
5、Serial Old收集器
Serial Old是 Serial收集器的老年代版本;
1、特点
针对老年代;
采用"标记-整理"算法(还有压缩,Mark-Sweep-Compact);
单线程收集;
主要用于Client模式;
而在Server模式有两大用途:
(A)、在JDK1.5及之前,与Parallel Scavenge收集器搭配使用(JDK1.6有Parallel Old收集器可搭配);
(B)、作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用(后面详解);
6、Parallel Old收集器
Parallel Old垃圾收集器是Parallel Scavenge收集器的老年代版本;
JDK1.6中才开始提供;
1、特点
针对老年代;
采用"标记-整理"算法;
多线程收集;
"-XX:+UseParallelOldGC":指定使用Parallel Old收集器;
7、CMS收集器
并发标记清理(Concurrent Mark Sweep,CMS)收集器也称为并发低停顿收集器(Concurrent Low Pause Collector)或低延迟(low-latency)垃圾收集器;
有4个过程组成:
1.初始标记
标记GC Root直接关联的对象,串行,会产生stop the world
2.并发标记
标记对象是否存活,与用户线程并行执行
3.重新标记
主要是重新标记在并发标记过程中新产生的对象(GC并行),会发生stop the world
4.并发清除
清除未标记的对象,与用户线程并行执行
串行
同一确定的时点只能有一个线程分配CPU(单核CPU)
并行
同一确定的时点有多个线程(GC线程)分配在不同的CPU上(多核CPU)
并发
同一确定的时点有多个线程(用户线程和GC线程)分配在不同的CPU上
优点:
并发收集器,用户线程和GC线程可以并行执行,从而尽可能降低用户线程的停顿时间
缺点:
1.会影响整体吞吐量和CPU性能
2.清理不彻底
2、应用场景
与用户交互较多的场景;
希望系统停顿时间最短,注重服务的响应速度;
以给用户带来较好的体验;
如常见WEB、B/S系统的服务器上的应用;
"-XX:+UseConcMarkSweepGC":指定使用CMS收集器;
8、G1收集器
G1(Garbage-First)是JDK7-u4才推出商用的收集器;
使用标记整理算法,不会产生内存碎片
特点:
1.可以精确设置垃圾收集的停顿时间
2.收集年轻代和老年代,同时G1会将堆划分为多个区域,并维护一个优先级列表,在容许的垃圾收集时间内优先收集优先级高的区域
-XX:+UseG1GC 开启G1收集器
-XX:+ConcGCThreads:并发GC线程数
-XX:+G1HeapRegionSize:G1区域大小 根据堆的最小值划分出2048个区域
面向服务端应用,针对具有大内存、多处理器的机器;
最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案;
如:在堆大小约6GB或更大时,可预测的暂停时间可以低于0.5秒;
在下面的情况时,使用G1可能比CMS好:
(1)、超过50%的Java堆被活动数据占用;
(2)、对象分配频率或年代提升频率变化很大;
(3)、GC停顿时间过长(长于0.5至1秒)。