[toc]
经典垃圾收集器
经典垃圾收集器指JDK7 Update4之后,JDK11正式发布之前,HotSpot虚拟机所包含的可用的垃圾收集器
Serial/Serial Old收集器
它们是最基础的收集器,Serial收集器面向新生代,Serial Old面向老年代,当垃圾收集器工作时,由单个线程进行所有收集工作,同时需要暂停所有用户线程。如上图所示,新生代采用标记-复制算法,老年代采用标记-整理算法。其优点是简单高效,且是所有垃圾收集器里额外内存开销最小的,面对几十甚至一两百兆的堆内存,其停顿时间也就在十几、几十毫秒内
ParNew收集器
ParNew收集器实质上是Serial收集器的多线程并行版本,它也是面向新生代,在进行垃圾收集时它采用多个线程同时工作,其余行为都与Serial收集器完全一致
Parallel Scavenge收集器
它也是面向新生代,但是更注重于服务的吞吐量,它会自动调节Eden区、Survivor区大小等参数,从而提供最大的系统吞吐量
Parallel Old收集器
它是Parallel Scavenge的老年代版本
CMS收集器
狭义上的CMS收集器是面向老年代的,其年轻代一般使用ParNew收集器进行收集。一般意义上我们指的CMS收集器指 ParNew + CMS + Serial Old。
如上图,CMS收集器主要的过程分为四步:初始标记、并发标记、重新标记、并发清除。它是面向延迟的收集器,只在初始标记和重新标记阶段需要停顿用户线程,其它阶段都是并发进行,而初始标记和重新标记阶段的停顿时间与堆内存的关系不大,因此相对可控。它的缺点是需要占用较多的系统资源、同时由于无法处理浮动垃圾(在并发收集时,可能产生新的垃圾对象),一方面需要在老年代预留部分空间用于存储这些本次收集过程无法处理完的浮动垃圾,另一方面需要用Serial Old收集器来进行兜底操作,当预留空间无法满足程序新分配对象的使用时,触发一次Serial Old收集来收集整个老年代空间。此外,由于CMS基于标记-清除算法,因此容易产生碎片内存,因此内存不够开始进行垃圾收集时,需要进行一次内存碎片的合并整理工作,该行为也会导致用户线程的停顿
Garbage First收集器
G1收集器是垃圾收集技术发展史上里程碑式的成果,它将内存分为一块块固定大小的Region,并将每个Region分为固定大小的Card。逻辑上每块Region可以分别归属于逻辑上的新生代或者老年代,在进行垃圾收集时以Region为最小处理单元,按回收受益和设定的预期停顿时间选择部分Region进行回收。
G1收集器工作过程大致分为四个步骤:
- 初始标记:标记GC Roots能直接关联到的对象,需要停顿用户线程
- 并发标记:根据引用关系扫描堆对象,与用户线程并发执行
- 最终标记:处理并发阶段发生引用改动的对象(采用原始快照法),需要停顿用户线程
- 筛选回收:筛选出具有高收益的Region进行回收,并将存活对象复制到空Region
为了处理跨代引用,每个Region都需要维护一个叫Rset的数据结构(其实就是个hash表,key为有指向本region的引用的对象所在Region的起始地址,value为对象所在Card的索引号集合)
G1的优点很明显,比如可以指定最大停顿时间、分Region的内存布局,按受益动态决定会收集合等。缺点则是Rset要占用10%-20%的堆内存,同时由于Rset的复杂性,以及原始快照法本身也带来更多运行负载,因此执行时的额外负载也更高。