上一节介绍的是垃圾回收算法,这一节介绍垃圾搜集器。何为垃圾收集器,其实就是讲之前的垃圾回收算法通过编程语言实现出来。
在介绍下面的回收器之前,先来接收两个概念,并发、并行、吞吐量:
并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
并发:指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。
吞吐量:吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%
下面根据上述的垃圾收集器来具体介绍与之对应的实现:
1. 串行搜集器(一个GC单线程,且会暂停用户程序)
实现:serial(用于新生代,采用复制算法)、serial old(用于年老代,采用标记/整理算法)。
优点:Serial收集器对于桌面应用交互程序和运行在Client模式下的虚拟机来说是一个很好的选择。
缺点:需要暂停用户线程,且停顿时间较长。
2. 并行收集器(多个GC线程,且会暂停用户程序)
实现:ParNew(用于新生代,采用复制算法)、Parallel Scavenge(用于新生代,采用复制算法,更关注吞吐量)、Parallel old(用于年老代,采用标记/整理算法)。
优点:在中到大型的堆上,且系统处理器至少多于一个的情况。
缺点:对于单个处理器来说,由于并行执行的开销(比如同步),ParNew的性能将会低于serial搜集器。不仅是单个处理器的时候,如果在容量较小的堆上,甚至在两个处理器的情况下,ParNew的性能都并非一定可以高过serial。
3.并发收集器(一个或多个GC线程,在需要的阶段暂停用户程序,不需要的阶段和用户程序并发执行)
实现:CMS收集器(用于年老代,采用标记/清除算法)。
优点:真正意义上实现了应用程序与GC线程一起工作(一起是针对客户而言,而并不一定是真正的一起,有可能是快速交替)的搜集器。重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验
CMS处理分为四个阶段:
1、初始标记:需要暂停应用程序,快速标记存活对象。
2、并发标记:恢复应用程序,并发跟踪GC Roots。
3、重新标记:需要暂停应用程序,重新标记跟踪遗漏的对象。
4、并发清除:恢复应用程序,并发清除未标记的垃圾对象。
缺点:在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低。其次,由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。最后一个缺点就是CMS使用的是标记清除法,会产生空间碎片,从而无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。
4.G1收集器(jdk1.7后出来,将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。)
上面介绍的6种垃圾搜集器都是针对不同内存区域设计的,我们需要给JVM的新生代和老年代选择相应的垃圾收集器,上面新生代垃圾搜集器有serial,ParNew和Parallel Scavenge三种,老年代垃圾搜集器有serial old,Parallel old和CMS三种。也就是说选择应该是3*3=9种,但是,事实上只有6种,因为有的垃圾搜集器由于具体实现方式等原因导致无法在一起工作。下图展示了可以组合工作的图。
六种组合是:serial-CMS、serial-serial old、ParNew-CMS、ParNew-Seria old、Parallel Scavenge-Serial Old、Parallel Scavege-Parallel old
下面简单介绍下其中的几种组合
1.serial-serial old:这个组合是最常见的,是client模式下默认的垃圾收集器组合,也可以使用参数-XX:+UseSerialGC强制开启。由于它实现相对简单,没有线程相关的额外开销(主要指线程切换与同步),因此非常适合运行于客户端PC的小型应用程序,或者桌面应用程序(比如swing编写的用户界面程序),以及我们平时的开发、调试、测试等。
2.Parallel Scavenge-Parallel old:这个组合不常见,但是如果对吞吐量要求比较高或对停顿时间要求高的应用程序来说,是首选,这个组合是server模式下默认的组合,可以利用-XX:+UseParallelGC参数强制开启。
3.ParNew-CMS(serial old):这个组合在平常开发中不常见,但是在对相应时间要求较高的应用程序来说,是首选,这个组合使用-XX:+UseConcMarkSweepGC开启。这个组合在新生代使用并行,因此新生代GC速度非常快,老年代使用并发,因此老年代停顿时间较短,适用于一些需要长期运行且对相应时间有一定要求的后台程序,最典型的的就是WEB应用程序。