【JAVA核心知识(面试宝典)】4.1: JVM GC垃圾收集器之Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old

    在【JAVA核心知识】2: JVM的垃圾回收与回收算法中可以看到GC分代收集算法将堆内存空间分为新生代和年老代两部分。新生代GC主要使用复制算法,年老代GC主要使用标记清除或者标记整理算法。JVM为新生代和年老代分别提供了多种垃圾处理器,开发者可以根据场景选择不同的垃圾处理器。目前HotSpot JVM的垃圾处理器种类如下:Serial垃圾收集器,ParNew垃圾收集器,Parallel Scavenge垃圾收集器,Serial Old垃圾收集器,Parallel Old垃圾收集器,CMS垃圾收集器,G1收集器。

    其中Serial垃圾收集器,ParNew垃圾收集器,Parallel Scavenge垃圾收集器适用于新生代。Serial Old垃圾收集器,Parallel Old垃圾收集器,CMS垃圾收集器适用于年老代。以上六种垃圾收集器均属于分代收集算法。而G1垃圾收集器则是分区收集算法下的垃圾收集器,也就没有适用于新生代还是年老代的概念了,它针对整个堆。
【JAVA核心知识(面试宝典)】4.1: JVM GC垃圾收集器之Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old_第1张图片
    本篇博文介绍Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old这5种相对简单的垃圾收集器。CMS与G1较为复杂,所需篇幅较大,后面分别单独以一篇介绍。

1:Serial垃圾收集器

关键字:单线程,复制算法,新生代
    Serial垃圾收集器采用复制算法。在JDK1.3之前唯一的新生代垃圾收集器,是最基本的垃圾收集器。
    Serial垃圾收集器采用单线程进行垃圾回收。在垃圾回收的过程中需要暂停所用的工作线程,也就是需要Stop The World直到收集结束。
【JAVA核心知识(面试宝典)】4.1: JVM GC垃圾收集器之Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old_第2张图片
    Serial垃圾回收器相对于其他垃圾回收器来说相对简单,因为采用单线程进行垃圾回收,所以在单CPU模式下,Serial垃圾回收器没有回收线程交互的开销,可以获得较高的单线程回收效率。同时对于内存空间较小的程序(如桌面程序内存一半在一两百兆),因为要回收的空间小,可以在较短的时间内完成收集,回收所造成的Stop The World是可以接受的。
    综合以上Serial垃圾收集器依然是JVM在Client模式下默认的新生代垃圾收集器。

2:ParNew垃圾收集器

关键字:多线程,复制算法,新生代
    ParNew垃圾收集器是Serial垃圾收集器的多线程版本,两个收集器共用了一部分代码,除了多线程外其它行为和Serial是相同的,采用复制算法。属于新生代垃圾收集器。在垃圾回收过程中同样需要Stop The Woerld。
【JAVA核心知识(面试宝典)】4.1: JVM GC垃圾收集器之Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old_第3张图片
    ParNew垃圾收集器默认启用和CPU数相同的GC线程[注2](可以通过-XX:ParallelGCThreads参数设置线程数量),CPU越多,堆内存越大,ParNew垃圾收集器的优势越明显[注1]。但是在单CPU情况下,ParNew收集器如果设定启用GC线程超过1个,那么因为线程交互的原因,性能上会比Serial垃圾收集器差。
    ParNew垃圾收集器是一个很重要的垃圾收集,因为它是除了Serial垃圾收集器外仅有的可以和CMS垃圾收集器配合工作的收集器[注3]。但是对于运行在Server模式下的JVM,因为Server模式的特性(一般都是多CPU,大内存空间),ParNew比Serial有更高的回收效率。
注1:为什么CPU越多,堆内存越大,多线程回收比单线程回收更具有优势呢?这是在多CPU下,因为需要Stop The World,而同一时间一个线程只会占用一个CPU,单线程模式下除了回收线程所占用的CPU,剩下的CPU只能处于空闲状态,这就造成了资源的浪费,而多线程则可以充分利用每个CPU都参与进回收工作中,随着CPU的数据增加,多线程线程交互的代价相对于单线程n-1个CPU空闲的代价会越来越小。单CPU的话一个GC线程即可以完整占用CPU又避免了线程交互的消耗,所以性能更佳。同样的堆内存越大意味着要回收的内存越多,多CPU,多线程比单线程用于更快的工作效率,GC所导致的Stop The World停顿也就更短。
注2:为什么默认启用和CPU数相同的GC线程呢?这是因为GC线程属于CPU密集型线程而非IO密集线程。
注3:为什么只有Serial与ParNew可以和CMS配合呢?这是因为Serial,ParNew和CMS都采用的分代式框架。而另外的可以用作新生代的收集器Parallel Scavenge为了吞吐量可控则是采用的adaptive size policy(自适应调整策略)。适用于整个堆的G1收集器则是采用的分区式框架。GC策略的不同导致无法相互配合使用.

3:Parallel Scavenge垃圾收集器

关键字:吞吐量可控,多线程,复制算法,新生代
    Parallel Scavenge垃圾收集器属于新生代垃圾收集器,采用复制算法,多线程收集。它关注的重点在吞吐量[注4]可控,采用的收集策略为adaptive size policy(自适应调整策略)。该策略下垃圾收集器会收集系统的性能信息,然后动态的调整新生代的大小以及晋升老年代所需年龄来控制吞吐量。具体表现为CPU繁忙时间尽量避免GC,或是控制GC时间尽量短,CPU空闲时则相对频繁的回收,同时也容许GC停顿(Stop The World)更长的时间。
    综合以上因素Parallel Scavenge垃圾收集器适用于偏向于后台运算,与用户交互较少的场景。因为高吞量可以更高效率的利用CPU的运行时间,使得运算效率更高。但是自适应调整策略在CPU空闲时所容许更大的停顿时间对于需要与用户交互的场景并不是一个好的机制。空闲时用户反而可能获得更慢的响应速度,这无疑会带来不好的用户体验。
注4:吞吐量(Thoughput)为CPU用于运行用户线程的时间与CPU总运行时间的比例。即吞吐量=CPU用于用户线程的时间/(CPU用于用户线程的时间+CPU用于GC的时间)。
注5:重要参数:
-XX:MaxGCPauseMillis:设置最大停顿时间,Parallel Scavenge垃圾收集会尽可能的控制停顿时间不超过这个时间。这个时间不是越小越好,更小的停顿时间也就意味着Parallel Scavenge为了满足要求会更频繁的进行GC。
-XX:GCTimeRatio:设置吞吐量。Parallel Scavenge会尽量使吞吐量达到要求。
-XX:+UseAdptiveSizePolicy:是否启用自适应调整策略。策略启用后就不需要设置新生代大小,晋升年龄等参数。Parallel Scavenge会根据性能信息进行自适应调整。

Serial Old垃圾收集器

关键字:单线程,标记整理,老年代
    Serial Old垃圾收集器是Serial垃圾收集器的老年代版本,与Serial垃圾收集器一样是单线程回收,不同的是采用的标记整理算法。
【JAVA核心知识(面试宝典)】4.1: JVM GC垃圾收集器之Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old_第4张图片
和Serial垃圾收集器一样,Serial也主要应用于Client模式。
在Server模式下,Serial Old的应用场景主要有:
1.在JDK1.5之前,与Parallel Scavenge垃圾收集器配合使用,作为年老代的垃圾收集。(JDK1.6之后有了Parallel Old垃圾收集器来配合Parallel Scavenge垃圾收集器)。
2.作为CMS垃圾收集器的后备预案。在CMS垃圾收集器并发收集过程中出现Concurrent Mode Failure[注6]时,老年代垃圾收集器会由CMS退化为Serial Old。
注6:Concurrent Mode Failure是CMS垃圾收集器特有的错误。CMS垃圾收集过程中,如果空间不足那么CMS就会进行Stop The World的Full GC,如果在Full GC进行过程中依然出现无法分配的对象的,就会产生这个错误。它表示CMS在清理过程中老年代的空间不足以容纳新的老年代。产生这个问题的原因主要有两个:
一是老年代的回收速度跟不上新的老年代产生的速度导致的空间不足。
二是有大对象被直接放入老年代导致的,这种情况有可能是空间不足,也有可能是老年代内存空间碎片化严重,导致没有足够的连续内存存放大对象。
解决空间不足的问题可以通过降低CMS GC的阀值提升回收频率或者调大老年代空间来解决。而空间碎片化严重的问题CMS的解决方案是在执行一定次数的Full GC之后执行一次标记整理算法以此来保持内存碎片率在一个可以接受的范围。可以通过-XX:UseCMSCompactAtFullCollection设置启用并通过-XX:CMSFullGCBeforeCompaction设置经历几次(默认是0)Full GC后执行标记整理算法。注意是CMS GC不足时导致的Full GC次数,而不是CMS GC次数。
值得注意的是一旦出现Concurrent Mode Failure,老年代垃圾收集器就会由CMS退化为Serial Old。这个过程是不可逆的。也就是说一旦退化,在程序重启之前,老年代会一直采用会产生Stop The World的Serial Old。因此应该避免出现Concurrent Mode Failure错误,如果出现也要及时处理。

Parallel Old垃圾收集器

关键字:多线程,标记整理
    Parallel Old垃圾收集器是Parallel Scavenge垃圾收集的的老年代版本,采用多线程的标记清理算法。重点一样在吞吐率可控。在JDK1.6及以后被提供。所以在JDK1.5之前只能采用Parallel Scavenge + Serial Old这种只能保证新生代吞吐率,无法保证老年代吞吐率的组合。
    Parallel Old垃圾收集器适用于Server模式。在多CPU,且偏向于后台运算这种注重吞吐率的场景下Parallel Scavenge + Parallel Old无疑是一种非常强力的组合。

参考资料:
一文了解JVM全部垃圾回收器,从Serial到ZGC
GC之ParNew收集器
Parallel Scavenge收集器
GC之Parallel Scavenge收集器
Serial Old收集器和Parallel Old收集器

你可能感兴趣的:(JAVA核心知识,JAVA,jvm,面试,java,GC)