前言
本文参考《深入理解Java虚拟机》一书,本文主要介绍几个经典的垃圾收集器:Serial、ParNew、parallelScavenge、CMS、Serial Old、Parallel Old、G1
本系列其他文章链接:
JVM(Java Virtual Machine)内存模型篇
JVM(Java Virtual Machine)垃圾收集算法篇
垃圾收集器
如果说收集算法是内存回收的方法论,那垃圾收集器就是内存回收的实践者。下图展示了7种不同的分点收集器,如果两个收集器之间存在连线,就说明它们之间可以搭配使用,图中收集器所处的区域,则表示他是属于新生代收集器抑或是老年代收集器。
新生代收集器
Serial
Serial收集器可以说是远古时期的收集器,它是一个单线程、标记-复制算法收集器,,但它的“单线程”不仅仅是指只会使用一个处理器或一条收集线程去处理垃圾收集
工作,更重要的是强调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束
。
暂停其他线程被称为:“STW(Stop the world)”,对于STW,就像你在房间里干大事,你老妈要进来打扫卫生,你也得乖乖到某个地方暂时待着等她收拾完
下图是Serial/Serial Old收集器运行过程:
Serial收集器在单核或者核心数比较少的系统中,Serial收集集由于没有线程交换的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。
ParNew
ParNew收集器实质上是Serial的多线程并行版本。除了是使用多线程收集垃圾外,其他收集算法、控制参数、STW、对象分配规则、回收策略等都和Serial一样。下图是ParNew/Serial Old的收集运行图:
ParNew默认开启线程数与处理器核心数量相同
ParNew 是一个低延迟、标记-复制算法的垃圾收集器,与后面会介绍的CMS收集器属于同一个类型,所以ParNew和CMS是可以互相搭配使用的,但是对于CMS来说,只有ParNew或者Serial能与它一起工作,其它收集器都不行。
Parallel Scavenge
Parallel Scavenge收集器也是一款新生代、并行、标记-复制算法的收集器,相比于其他垃圾收集器关注低延时,这款关注的是 “吞吐量”
如果虚拟机完成某个任务,用户代码+垃圾收集 == 100分钟,其中垃圾收集花掉1分钟,那么吞吐量就是99%。
低延时: 适合于用户交互或者需要保证服务响应质量的程序,良好的响应可以提升用户体验。
高吞吐: 可以高效率地利用处理器资源,尽快完成程序的运算任务,主要适合后台运算而不太需要太多交互的分析任务
所以由于定位不同,Parallel Scavenge无法和追求低延时的垃圾收集器合作
老年代收集器
Serial Old
Serial Old收集器是Serial的老年代版本,收集算法上使用的是标记-整理算法。
当然他也可以和Parallel Scavenge一起使用,除此之外,它也是CMS的备用方案,当CMS回收失败的时候,就需要启动Serial Old来进行垃圾收集了。
Parallel Old
Parallel Old是Parallel Scavenge的老年代版本,自然也是以吞吐量为目标,使用的是标记-整理算法。Parallel Scavenge/Parallel Old的收集流程
CMS
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,这个算法是使用的标记-清理算法
它的运作过程稍微复杂一点,包括下面四个步骤:
- 初始标记(CMS initial mark)
- 并发标记(CMS concurrent mark)
- 重新标记(CMS remark)
- 并发清除(CMS concurrent sweep)
其中初始标记、重新标记这两个步骤还是会经历STW,但是时间非常短
- 初始标记:仅仅是为了标记一下“GC Roots”能直接关联到对象”
- 并发标记:从GC Roots的直接关联对象开始遍历整个对象图的过程
- 重新标记:为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的一步分对象进行标记记录
- 并发清理:清理删除掉标记阶段判断的以及死亡的对象,由于不需要移动存活对象,所以这个阶段也是与用户线程并发的。
下图为收集过程图:
CMS虽然并不会导致用户线程的停顿,但却也会一定程度上降低应用程序效率,毕竟也分配一部分资源出去了。默认的CMS默认启动的回收线程数是(处理器核心数量+3)/4。
为了高效率,自然有会在内存空间上有所牺牲,CMS无法处理“浮动垃圾”,也就是在并发标记和并发清理的过程中产生的新垃圾,这些垃圾只能留到下一次老年代发生垃圾收集才能被回收。除此之外,CMS使用标记-清理算法也会导致内存碎片问题。
因此CMS收集器就必须为老年代预留一些空间来暂时存放这些“浮动垃圾”,当CMS运行期间预留的内存无法满足程序分配新对象,就会出现以一次“并发失败”,这时候就会使用上面提到的Serial Old这个备用方案,对老年代进行一次彻底的清理,且将内存碎片问题解决。(Serial Old为标记整理算法,CMS为标记清理算法)
虽然如此弊端,但是不影响CMS是一款优秀的垃圾收集器,是目前任然应用在大多数基于浏览器B/S系统的服务端上。
G1收集器
如果要讲述这个收集器篇幅过大,将来有时间再补上(并非偷懒T_T)