第二部分说了垃圾收集算法,那么紧接着就应该是具体如何实现这些算法?谁来执行?答案是:垃圾收集器
如图所示,下图展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,就说明他们可以搭配使用。
在这7种垃圾收集器中,用于新生代的有:
Serial、ParNew、Parallel Scavenge
用于老年代的有:
CMS、Serial Old、Parallel Old
注意:当新生代用Parallel Scavenge时,老年代必须用Parallel Old
逐个说明一下:
1、Serial
顾名思义,这是一个单线程的收集器(serial是连续、串行的意思)
“单线程”的意义不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。
显然这对用户来说是个非常不好的体验,在用户不可见的情况下把用户的工作线程全部停掉。
对此,虚拟机设计者给了一段非常形象的描述:
“你妈妈给你打扫房间的时候,肯定也会让你老老实实在椅子上坐在或者房间外面呆着,如果她一边打扫,你一遍扔纸屑,这房间还能打扫完?”
但是这种垃圾收集器非常简单而高效,对于限定单CPU的环境来说,Serial收集器没有线程交互的开销,收集效率很高。适用于Client模式下。
2、ParNew
顾名思义,就是多线程版本的Serial收集器(Par是单词Parallel的缩写),它的特点是可以和CMS收集器配合工作。适合在多CPU环境中使用,默认开启线程数与CPU的数量相同,可通过参数修改。
3、Parallel Scavenge
Parallel Scavenge收集器是一个新生代收集器,也是使用复制算法的收集器,同时也是多线程收集器。与其他收集器不同的是,它侧重点在于达到一个可控的吞吐量(Throughput)
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
因此它也被称为“吞吐量优先”收集器,高吞吐量可以高效利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是最大垃圾收集停顿时间和吞吐量大小。并且具有自动调节策略,打开开关后,收集器会动态调节这些参数。
4、Serial Old
Serial Old是Serial收集器的老年代版本,同样是一个单线程收集器,使用“标记-整理”算法。它主要也是给Client模式下的虚拟机使用。
5、Parallel Old
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。
在注重吞吐量以及CPU资源敏感的场合,都可以考虑使用Parallel Scavenge加Parallel Old收集器。
6、CMS收集器
这是一种以获取最短回收停顿时间为目标的收集器。
它的运作过程分为四个阶段:
(1)初始标记
(2)并发标记
(3)重新标记
(4)并发清除
其中,初始标记和重新标记这两个步骤仍然需要停顿。初始标记只是标记一下能被GC Roots直接关联到的对象,速度很快。并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长,但远比并发标记的时间短。
即使是在耗时最长的并发标记和并发清除过程,收集器线程都可以与用户线程一起工作。
因此,他具有明显的优点:并发收集、低停顿
同时也具有一定缺点:
(1)对CPU资源敏感。因为是并发运行的,占用了一部分线程资源,导致应用程序变慢,总吞吐量降低。
(2)无法处理浮动垃圾。因为是并发收集的,相当于“妈妈一边打扫房间,孩子一边扔纸屑”,收集期间新产生的垃圾只能等下次垃圾回收时候处理。并且垃圾回收期间也要预留足够的内存空间给用户线程使用。
(3)CMS是基于“标记-清除”算法实现的收集器
这种算法会产生空间碎片,碎片较多时候,会给大对象分配带来麻烦。解决方法是,执行了几次不压缩的垃圾回收后,跟着来一次带压缩的垃圾回收。
7、G1收集器
G1是一款面向服务端应用的垃圾收集器,与CMS相比,G1主要特点是:
(1)并行与并发:更充分利用CPU、多核环境下硬件优势,缩短停顿时间。
(2)分代收集:保留了分代概念,但是G1不需要其他收集器配合,独自管理整个堆内存
(3)空间整合:基于“标记-整理”算法,避免空间碎片,有利于程序长时间运行
(4)可预测的停顿:建立了可预测的停顿时间模型,让用户明确指定停顿时间
它将整个java堆划分为多个大小相等的独立区域(Region),新生代和老年代都不再是物理隔离,而是一部分Region的集合。
它在后台维护了优先列表,在每次允许的收集时间内,优先收集价值最大的Region,根据用户所期望的GC停顿时间来制定回收计划。
另外还维护了Remembered Set来记录不同Region中对象的互相引用关系,从而避免了做可达性分析时全堆扫描。每个Region都有一个与之对应的Remembered Set。