如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商、不同版本的虚拟机所提供的垃圾收集器都可能会有很大差别,并且一般都会提供参数供用户根据自己的应用特点和要求组合出各个年代所使用的收集器。这里讨论的收集器基于JDK 1.7 Update 14之后的HotSpot虚拟机 (在这个版本中正式提供了商用的G1收 集 器 ,之前G1仍处于实验状态 )。下面对几种收集器进行详细介绍。
1. Serial收集器(串行GC)
Serial收集器是最基本,历史最悠久的收集器,是一个单线程的收集器。“单线程”的意义不仅仅是说明它只会用一个CPU或单线程进行收集工作,更重要的是在收集过程中必须暂停其他所有工作线程(“stop the world”),直到收集结束。
实际上到现在为止,它依然是虚拟机运行在Client模式下的默认新生代收集器。
它也有着优于其他收集器的地方:简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。在用户的桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大 ,收集几十兆甚至一两百兆的新生代(仅仅是新生代使用的内存,桌面应用基本上不会再大了 ) ,停顿时间完全可以控制在几十毫秒最多一百多毫秒以内 ,只要不是频繁发生 ,这点停顿是可以接受的。所 以 ,Serial收集器对于运行在Client模式下的虚拟机来说是一个很好的选择。
2. ParNew收集器 (并行GC)
它是运行在Server模式下虚拟机的首选新生代收集器,有一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。
3. Parallel Scavenge
Parallel Scavenge是并行多线程的新生代收集器,采用复制算法收集。Parallel
Scavenge收集器关注的目标是达到一个可控制的吞吐量(吞吐量 = 用户代码运行时间/(用户代码运行时间+垃圾收集时间)),其他收集器关注于尽可能缩短垃圾回收时用户线程的暂停时间。停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效率地利用CPU时间 ,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。Parallel
Scavenge收集器无法与CMS收集器配合工作。
4. Serial Old收集器
Serial的老年代版本,也是单线程收集器,采用标记-清理算法。主要是被Client模式下的虚拟机使用。
5. Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。这个收集器是在JDK 1.6中才开始提供的,在此之前,新生代的Parallel Scavenge收集器一直处于比较尴尬的状态。原因是 ,如果新生代选择了Parallel Scavenge收集器 ,老年代除了 Serial
Old ( PS MarkSweep ) 收集器外别无选择(还记得上面说过Parallel Scavenge收集器无法与CMS收集器配合工作吗)。由于老年代Serial Old收集器在服务端应用性能上的“拖累” ,使用了Parallel Scavenge收集器也未必能在整体应用上获得吞吐量最大化的效果,由于单线程的老年代收集中无法充分利 用服务器多CPU的处理能力,在老年代很大而且硬件比较高级的环境中,这种组合的吞吐量甚至还不一定有ParNew加CMS的组合“给力”。
ParNew收集器就是Serial收集器的多线程版本,除了使用多线程收集之外,其余行为(参数设置,收集算法,stop world,对象分配规则,回收策略等)都一致。
6. CMS(Concurrent Mark Sweep)收集器(老年代)
- 初始标记( CMS initial mark ):标记GC Roots能直接关联到的对象,需要stop world
- 并发标记( CMS concurrent mark ):进行GC Roots Tracing的过程
- 重新标记( CMS remark ):修正并发标记时间,因为用户线程继续运行,导致标记变化,需要stop world
- 并发清除( CMS concurrent sweep )
CMS还远达不到完美的程度,它有以下3个明显的缺点:1.对CPU敏感;2.CMS无法处理浮动垃圾(由于CMS并发处理阶段,用户线程还在运行,会不断地有新垃圾产生);3. 标记—清除会有大量的空间碎片。
7. GI(Garbage First)收集器
G1 (Garbage-First)收集器是当今收集器技术发展的最前沿成果之一,早在JDK 1.7刚刚确立项目目标,Sun公司给出的JDK 1.7 RoadMap里面,它就被视为JDK 1.7中HotSpot虚拟机的一个重要进化特征。从JDK 6u14中开始就有Early Access版本的G1收集器供开发人员实验、试用,由此开始G1收集器的“Experimental”状态持续了数年时间,直至JDK 7u4 , Sun公 司才认为它达到足够成熟的商用程度,移除了“Experimental”的标识。
- 并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU ( CPU或者CPU核心)来缩短Stop-The-World停顿的时间,部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。
- 分代收集:与其他收集器一样,分代概念在G1中依然得以保留。虽然G1可以不需要其他收集器配合就能独立管理整个GC堆 ,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
- 空间整合:与CMS的“标记一清理”算法不同,G1从整体来看是基于“标记一整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。
- 可预测的停顿:这是G1相对于CMS的另一大优勢,降低停顿时间是G1和CMS共同的关注点 ,但G1除了追求低停顿外 ,还能建立可预测的停顿时间模型 ,能让使用 者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒 ,这几乎已经是实时Java ( RTSJ ) 的垃圾收集器的特征了。