首先总结下垃圾收集器都有哪些:
- 回收年轻代的垃圾收集器:Serial、ParNew、Parallel Scavenge。
- 回收老年代的垃圾收集器:CMS、Serial Old、Parallel Old。
- 同时回收老年代和年轻代的:G1。
没有一个收集器可以作用于所有的应用场景,只有具体应用选择具体合适的收集器,正是因为如此才会有各种收集器的存在。
Serial收集器
该收集器从名字就可以看出来它为串行垃圾收集器,就是在该收集器工作的时候会暂停掉所有的用户线程,直到收集器工作完成才会正常运行用户线程。serial搭配serial old收集器运行如下:
- 缺点:回收需要暂停所有用户线程直到回收结束,暂停时间长。
- 优点:实现相对简单,对于资源紧张的服务比较好,是客户端模式下默认的垃圾收集器。
ParNew收集器
ParNew是Serial的多线程版本,除了多线程外其它基本上是Serial的复制品,ParNew工作图为:
如果将CMS作为老年代收集器的话,除了Serial外,只有它可以和CMS收集器配合使用。ParNew收集器是激活CMS后(使用-XX:+UseConcMarkSweepGC选项)的默认新生代垃圾收集器,也可以使用-XX:+/-UseParNewGC选项来强制指定或禁用它。
垃圾收集中并发和并行的概念
- 并行:并行描述的是多条垃圾收集器线程之间的关系,说明同一时间有多条这样的线程在工作,通常在这个时候用户线程被暂停处于等待状态。
- 并发:并发描述的是垃圾收集线程和用户线程之间的关系,说明同一时间垃圾收集线程和用户线程都在运行。由于用户线程和垃圾收集线程同时工作,此时垃圾收集线程会占用一定的处理器资源,这会导致系统的整体吞吐量下降。
Parallel Scavenge收集器
该收集器是以吞吐量优先的并行收集器,就是尽量降低系统的总暂停时间,提高系统的吞吐量就需要减少垃圾收集的停顿频率,频率多会导致系统的总吞吐量降低,频率低可以提高系统的吞吐量,但是伴随而来就是会导致系统的响应时间降低,所以二者不可兼得。
Parallel Scavenge有个-XX:+UseAdaptiveSizePolicy的参数,如果该参数打开后,就不要人工调节新生代大小(-Xmn)、Eden和Survivor区的大小比例(-XX:SurvivorRatio)、晋升老年代对象大小(-XX:PretenureSizeThreshold)等细节参数,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间和吞吐量,这个也是它和ParNew收集器的主要区别。
Serial Old收集器
Serial Old是Serial收集器的老年代版本,它同样是个单线程收集器,使用标记整理算法。
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,支持多线程并行收集,基于标记-整理算法。在注重吞吐量优先的应用当中,Parallel Scavenge和Parallel Old是很好的组合。
CMS收集器
CMS收集器是一种以获取最短回收停顿时间为目标的收集器,也就是它主要关注的是应用的响应时间,基于标记-清除算法,真正的并发垃圾收集器,也就是垃圾收集线程和用户线程并发运行,它回收可以总结为四个过程:
- 初始标记。
- 并发标记。
- 重新标记。
- 并发清除。
初始标记和重新标记仍然需要Stop the world,初始标记只是标记下与GC Roots能直接关联的对象;并发标记阶段就是从直接关联的对象开始标记整个对象图的过程,比较耗时,不过可以和用户线程并发执行;重新标记是为了标记哪些在用户线程并发运行过程中改变了的引用对象;并发清除就是清理掉已经死亡的对象,由于此过程不需要移动存活对象,所有可以和用户线程并发执行。CMS默认启动的回收线程数为(处理器核心数 + 3)/ 4;
缺点:并发标记过程会出现浮动垃圾,就是用户线程在并发执行那就会产生新的不用的对象这些就是浮动垃圾,这些垃圾需要在下次触发垃圾回收时才能被清除掉。由于用户线程和垃圾收集线程在并发执行,所以垃圾收集线程会占用一部分处理器资源,会导致应用的吞吐量下降;还有就是CMS收集器和用户线程一起执行,所以需要预留出来一定的空间来为用户线程分配对象,不可能像其他收集器一样等到老年代空间已满的情况下再回收,也就是在老年代已用空间达到某个百分比时就需要开始对象回收工作,如果并发回收过程中,新的对象过来没法分配空间,就会触发保护机制,也就是触发Serial Old收集器收集,这样停顿时间会直接上升,需要根据具体应用的情况来调整这个阈值。另外就是由于CMS是基于标记-清除算法来实现的,所有在每次垃圾收集完了后会产生内存碎片的问题,这个可以通过一个参数来调整-XX:CMSFullGCsBeforeCompaction的值来决定进行几次Full Gc后会触发一次内存整理的工作。