收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。我们讨论的收集器基于JDK 1.7 Update 14之后的HotSpot虚拟机(在这个版本中正式提供了商用的G1收集器,之前G1仍处于实验状态),这个虚拟机包含的所有收集器如下图所示。
一、Serial收集器
Serial收集器是一个作用于新生代的‘单线程’的收集器,采用标记复制算法,这里单线程不仅代表它只有一条线程进行垃圾回收,还代表这个收集器工作时,必须stop the world(STW),用户线程全部暂停,直到它收集结束。优点是简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。 在用户的桌面应用场景中, 分配给虚拟机管理的内存一般来说不会很大, 收集几十兆甚至一两百兆的新生代( 仅仅是新生代使用的内存,桌面应用基本上不会再大了),停顿时间完全可以控制在几十毫秒最多一百多毫秒以内,只要不是频繁发生,这点停顿是可以接受的。所以, Serial收集器对于运行在Client模式下的虚拟机来说是一个很好的选择。
开启参数:-XX:+UseSerialGC
适用范围:用户的桌面应用场景(IDE工具的应用场景)
运行过程如下图:
二、ParNew收集器
ParNew收集器,作用于新生代,采用标记复制算法,简单的理解就是Serial收集器的多线程版本,在回收时有多个线程同时进行垃圾回收工作,其余的基本与Serial收集器一致。但它却是许多运行在Server模式下的虚拟机中首选的新生代收集器,其中有一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。
开启参数:-XX:+UseParNewGC
适用范围:Server首选的新生代收集器
工作过程如下图:
三、Parallel Scavenge收集器
Parallel Scavenge收集器是一个新生代多线程收集器,其最重要的一个特性是用户可以控制吞吐量。控制参数为
-XX:MaxGCPauseMillis(最大垃圾收集停顿时间/ms);-XX:GCTimeRatio(吞吐量百分比,0-100之间);通过以上两个参数,Parallel Scavenge收集器可以控制吞吐量的大小,具体的吞吐量计算公式为:运行用户代码的时间/(运行用户代码的时间+GC时间)例如吞吐量是99%,那么99%的时间是用户线程执行的时间,1%的时间是GC的时间。垃圾收集停顿时间越短,响应速度越快,用户体验越好。但是不要盲目的减小垃圾收集的停顿时间,还是需要结合系统自身,完成一次垃圾收集需要的时间来设定,否则得不偿失。
开启参数:-XX:+UseParallelGC
适用范围:后台计算不需要太多交互的场景
四、Serial Old 和 Parellel Old
分别是Serial收集器和Parellel Scavenge的老年代收集器版本,使用标记-整理算法进行垃圾回收。
五、Concurrent Mark Sweep收集器
CMS收集器,作用于老年代区域,是一种以获取最短回收停顿时间为目标的收集器,基于比标记-清除算法实现,整个过程分为4步,初始标记(单线程,STW)、并发标记(多线程)、重新标记(多线程,STW),并发清除(多线程)。特点为低停顿,在互联网站和Web服务端适用广泛。
该收集器有三个可设置的的参数:
1)-XX:CMSInitiatingOccupancyFraction=70:代表老年代中占用70%的空间就会启用CMS收集器。
2)-XX:+UseCMSCompactAtFullCollection:CMS是不会移动内存的, 因此, 这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片。
3)-XX:+CMSFullGCsBeforeCompaction:设置多少次GC后进行内存压缩。
开启参数:-XX:+UseConcMarkSweepGC
工作流程如下图:
六、G1收集器
G1(Garbage-First)收集器是当今收集器技术发展的最前沿成果之一,是一款面向服务端应用的垃圾收集器,是目前唯一一个能在新生代和老年代都能使用的垃圾收集器,基于标记-整理和复制算法,整体流程分为下面几步,初始标记,并发标记,最终标记,筛选回收。G1收集器Java堆的内存布局就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。
工作流程如下图:
七、总结
并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
并发:指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。