垃圾收集器是内存回收的具体实现。以下讨论的收集器是基于JDK1.7Update14之后的HotSpot虚拟机。这个虚拟机包含的所有收集器有:
上图展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,就说明它们可以搭配使用。虚拟机所处的区域,则表示它是属于新生代收集器还是老年代收集器。接下来笔者将逐一介绍这些收集器的特性、基本原理和使用场景,并重点分析CMS和G1这两款相对复杂的收集器,了解它们的部分运作细节。
直到现在为止还没有最好的收集器出现,更加没有万能的收集器,所以我们选择的只是对具体应用最合适的收集器。如果有一种放之四海、任何场景下都适用的完美收集器存在,那HotSpot虚拟机就没必要实现那么多不同的收集器了。
1.1.1 Serial 收集器Serial收集器是最基本、发展历史最悠久的收集器,在JDK1.3.1之前是虚拟机新生代收集的唯一选择。
是单线程的收集器,进行垃圾收集时,必须 暂停其他所有的工作线程,直到它收集结束。缺点:用户停顿很长
运行过程:
新生代采用 复制算法
从JDK1.3开始,HotSpot虚拟机开发团队为消除或者减少工作线程因内存回收而导致停顿的努力一直在进行着,从Serial收集器到Parallel收集器,
再到Concurrent Mark Sweep(CMS)乃至GC收集器的最前沿成果Garbage First(G1)收集器,用户停顿在不断缩短。
但是依然是虚拟机运行在Client模式下的默认新生代收集器。
优点:简单而高效,对于限定单个CPU的环境来说,Serial 收集器由于没有现成交互的开销,专心做垃圾收集自然获得最高的单线程收集效率。
在用户桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大,收集几十M甚至一两百M的新生代,停顿时间完全可以控制在几十毫秒最多一百多毫秒以内,只要不频繁发生,这点停顿是可以接受的。
所以Serial收集器对于运行在Client模式下的虚拟机来说是一个很好的选择。
1.1.2 ParNew 收集器ParNew是Serial的多线程版本。复制算法
除了使用多条线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio、-XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一样,在实现上,这两种收集器也共用了相当多的代码。
ParNew收集器的工作过程:
它却是许多运行在Server模式下的虚拟机中首选的新生代收集器,其中有一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。
ParNew收集器也是使用-XX:+UseConcMarkSweepGC选项后的默认新生代收集器,也可以使用-XX:+UseParNewGC选项来强制指定它。
ParNew收集器在单CPU的环境中绝对不会有比Serial收集器更好的效果,甚至由于存在线程交互的开销,当然,随着可以使用的CPU的数量的增加,它对于GC时系统资源的有效利用还是很有好处的。它默认开启的收集线程数与CPU的数量相同,在CPU非常多(譬如32个,现在CPU动辄就4核加超线程,服务器超过32个逻辑CPU的情况越来越多了)的环境下,可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。
1.1.3 并行和并发收集器 概念解释并行 Parallel:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
并发 Concurrent:指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。
1.1.4 Parallel Scavenge 收集器
Parallel-Scavenge是一个新生代收集器,它也是使用复制算法的收集器,又是并行的 多线程收集器。