垃圾收集器
针对于具体的情况,JVM使用不同的垃圾收集器。按照不同分代,HotSpot使用了七种垃圾收集器:
① 新生代:Serial,ParNew,Parallel Scavenge
② 老年代:CMS,Serial Old(MSC),Parallel Old
③ 通用:G1
如图。连线表示可搭配使用。
1. Serial收集器
Serial收集器是最基本的收集器,是一个单线程收集器。运行时只会用一个cpu或一条线程来执行。其原理是运行时暂停其他所有的工作线程,直到收集结束。其缺点就是暂停时间过长。使用复制算法,用于新生代垃圾的收集。
目前所有的收集器都存在用户线程停顿的问题。只是停顿时间在不断缩短,但依然无法完全消除。
Serial收集器不会进行线程交互,是单线程收集器中最简单高效的收集器。收集一两百兆内的新生代耗时可控制在一百多毫秒内,是Client模式下的默认新生代收集器。
2. ParNew收集器
ParNew收集器是Serial收集器的多线程版本。
由于可与CMS搭配的收集器只有Serial和ParNew,所以ParNew是Server模式下JVM首选的新生代收集器。
注意在单核单线程模式下,ParNew效率<=Serial效率。因为ParNew是允许线程交互的。
3. Parallel Scavenge收集器
Parallel Scavenge收集器与ParNew收集器基本相同。但Parallel Scavenge收集器关注的是如何达到一个可控的吞吐量(1-垃圾收集时间/JVM运行总时间)。
吞吐量高并不意味着用户等待时间短,但可以高效地利用CPU时间从而尽快完成运算任务。所以Parallel Scavenge适用于后台不需要太多交互的任务。
Parallel Scavenge是新生代收集器。
4. Serial Old收集器
Serial Old收集器是Serial收集器的老年代版本,同样是单线程的。使用的是“标记-整理”算法。主要是用于Client模式下的JVM。
5. Parallel Old收集器
Parallel Old收集器是Parallel Scavenge的老年代版本。同样使用多线程和“标记-整理”算法。
Parallel Old的产生是为了配合Parallel Scavenge。这样,在注重吞吐量和CPU资源的场景就可优先考虑ParallelScavenge和Parallel Old组合。
6. CMS收集器
CMS收集器的目的是为了将GC回收时造成的停顿减到最小,从而带给用户最佳的体验。所以CMS往往用在互联网站或者B/S系统服务端上。
CMS使用更为复杂的“标记-清除”算法:
① 初始标记:暂停所有线程,标记一下GC Roots所能直接关联到的对象,用时非常短。
② 并发标记:进行GC Roots枚举,不需要暂停所有线程。这意味着用户在此期间可进行操作,于是前一阶段的对象标记有可能受到用户操作影响而变动。这个阶段用时较长。
③ 重新标记:暂停所有线程,修正并发标记期间由于用户操作导致变动的标记记录。用时比初始标记要长,但远低于并发标记。
④ 并发清除:清除失效对象。由于是与用户线程一起并发执行,所以耗时较长。
CMS支持并发收集,低停顿,但也有3个明显缺点:
① 对CPU资源敏感。默认的GC线程数是(CPU数量+3)/4。当CPU数量低于4个时,CMS对用户程序的影响就会变得很大。
② 由于CMS的清理是并发的,所以一边是GC在清理,一边是用户线程在产生垃圾。清理时新产生的垃圾就是“浮动垃圾”。CMS是无法处理的。
③ CMS使用“标记-清除”算法。所以清理完成后会产生大量空间碎片。
7. G1收集器
G1收集器是面向服务端应用的垃圾收集器。其目标是替换掉CMS收集器。
G1收集器有如下特点:
① 持并行和并发。
② 保留分代收集。同时将Java堆分多个独立区域(Region),新生代和老年代不再物理隔离。每代都是一部分区域的的集合(不需要连续)。G1收集时,会按价值对Region来进行逐个回收。所以,G1的回收并不是在整个Java堆中进行全区域的收集。
③ 整体使用“标记-整理”算法,局部使用复制法。有效避免内存碎片。
④ 停顿可预测。
类似CMS,G1的运行有以下几个步骤:
① 初始标记
② 并发标记
③ 最终标记
④ 筛选回收
唯一与CMS有区别的就是步骤④。筛选回收会对各个Region的回收价值和成本排序,并根据用户期望的GC停顿时间来制定回收计划。