JVM探秘4---垃圾收集器介绍

Java虚拟机有很多垃圾收集器

下面先来了解HotSpot虚拟机中的7种垃圾收集器:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1,先介绍一些垃圾收集的相关概念,再介绍它们的主要特点、应用场景、以及一些设置参数和基本运行原理。

常见的垃圾收集器关系图如下:

如果两个收集器直接有连线,则表示可以搭配使用,而G1收集器是不区分老年代和新生代的,所以不需要和其他收集器搭配,其他的则只能用于老年代或是新生代。

下面针对每个收集器逐个分析:

1.Serial收集器(新生代收集器)

Serial是历史最悠久的收集器,也一个单线程收集器,只有一个CPU或一个线程来完成垃圾收集工作,而且Serial收集器在垃圾回收的时候会暂停所有工作线程,直到垃圾回收结束再恢复工作线程。

2.ParNew收集器(新生代收集器)

ParNew收集器是Serial收集器的多线程版本,工作方式和工作原理完全一致,只是ParNew收集器会创建多个线程来进行垃圾回收工作。不过如果是在单CPU的情况下,ParNew收集器的性能反而不一定有Serial收集器好,因为ParNew还有额外的线程之间切换的消耗,如果是多CPU的情况下,则效果更好点,所以ParNew默认开启的垃圾收集的线程个数一个和CPU的数量是1:1的比例,在线程数比CPU数量小的情况下,ParNew的性能还是很可观的。

3.Parallel Scavenge收集器(新生代收集器)

Parallel Scavenage收集器是采用了复制算法,且是并行的多线程收集器,它的特点是不关注收集器用户线程的停顿时间,而是关注达到一个可控制的吞吐量。吞吐量为CPU用于运行用户线程的代码与CPU总耗时的比例,比如JVM工作100分钟,垃圾回收使用1分钟,用户线程使用99分钟,则吞吐量就是99%,Paramllel Scavenge收集器提供两个参数用户控制吞吐量。分别是控制最大垃圾收集停顿时间 -XX:MaxGCPauseMillis参数、设置吞吐量大小-XX:GCTimeRatio

如果设置停顿时间过短,则垃圾收集时花费的时间不会超过最大值,但是如果停顿时间过于小,就会导致新生代的垃圾回收的频率会增高,本来可以10秒收集一次,停顿100毫秒,变成了5秒收集一次,每次停顿70毫秒,虽然停顿时间降低了,但是整体的吞吐量却下降了。

而如果直接设置吞吐量,则收集器会按照吞吐量设置的值来分配整体的垃圾收集的时间来进行收集垃圾。

4.Serial Old收集器(老年代收集器)

Serial Old是Serial收集器的老年代版本,也是单线程的,使用的“标记-整理”算法

5.Parallel Old收集器(老年代收集器)

Parallel Old是Paraller Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法,只能与Parallel Scavenge收集器搭配使用

6.CMS收集器(老年代收集器)

CMS是目前最常用的收集器,主要是和ParNew来搭配使用。CMS(Concurrent  Mark Sweep)从名字可以看出是并发的标记清除垃圾收集器,过程比较复杂。特点是以获取最短回收停顿时间为目标,整个过程大致分为四个步骤:

初始标记-》并发标记-》并发预处理-》重新标记-》并发清除

初始标记:标记一下GC Roots能够关联到的对象,速度很快,但是也会使用户线程短暂的停止,只是停顿的时间很快。

并发标记:并发标记耗时较长,但是可以和用户线程并发进行,不需要使用户暂停,不过会消耗CPU的资源,会影响到用户线程的吞吐量,CMS默认会开启回收线程的数量是CPU个数+3/4,比如cpu是1的情况下,会启动一个线程,如果cpu是5的话,就会启动2个线程,并发标记会根据初始标记出来的通过GC ROOTS关联到的对象,然后通过递归继续标记这些对象可以达到的对象

重新标记:暂停用户线程,重新在堆中进行可达性分析,标记存货对象,此时的存活又没有被标记的对象很少了,所以用户线程停顿的时间也较短

并发清除:和用户线程并发进行,清除没有被标记的对象

另外CMS收集器无法处理浮动垃圾,因为cms和用户线程是并发进行的,在垃圾回收的同时用户线程可能会产生新的垃圾,这就使得本次垃圾回收无法清除,会留到下一次回收。另外cms是和用户线程并行处理的,所以在工作时还需要预留足够的内存空间在用户线程使用,因此cms收集器不能像其他收集器一样等到老年代几乎满了才开始进行垃圾回收,需要预留一部分给用户线程使用,默认情况下在老年代使用了68%空间之后就会触发垃圾回收。当然也可以通过设置jvm参数-XX:CMSInitatingOccupancyFraction来设置触发的百分比。

而如果CMS没有预留足够空间给用户线程的话,会出现“Concurrent Mode Failure”失败,这是虚拟机就会启动预备方案,临时启用Serial Old收集器来重新对老年代进行一次垃圾收集,这样就会影响性能。

而且由于CMS是通过标记清除算法,所以会产生大量的内存碎片,就会出现老年代内存空间足够但是无法创建一些大对象,就会提前触发一次Full GC。而CMS就提供了两个JVM参数用来优化这个问题,分别是:

-XX:+UseCMSCompactAtFullCollect参数,使得在实现Full GC之后额外进行一次内存整理,这样就没有了内存碎片,但是涉及到内存整理,所以停顿时间变长了

-XX:CMSFullGCsBeforeCompaction参数,设置在执行多少次不压缩的Full GC之后进行一次内存整理。

7.G1收集器  (不区分生命周期收集器)

G1收集器没有新生代和老年代的区分,G1实现了不牺牲吞吐量的情况下又达到了低停顿的效果。主要是因为G1不像其他收集器一样针对整个新生代或是老年代进行垃圾回收,而且将整个堆内存划分成了多个大小一样的独立区域,并跟踪每个区域的垃圾情况,并且有一个优先列表,优先回收垃圾最多的区域。

G1收集算法从JDK9开始就是JVM的默认GC垃圾收集器

8、ZGC收集器(不区分生命周期收集器)

 

posted @ 2018-09-04 19:29  Lucky帅小武  阅读(288)  评论(0编辑  收藏  举报