如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。虽然对各个收集器进行比较,但并非为了挑选出一个最好的收集器。因为直到现在为止没有出现一个最用的的垃圾收集器,更加没有万能的垃圾收集器,能做的就是根据具体应用场景选择适合自己的垃圾收集器。试想一下:如果有一任何场景下都适用的完美收集器存在,那么Java虚拟机就不会实现那么多不同的垃圾收集器了。
上图虚线上方是年轻代垃圾收集器,下方是老年代垃圾收集器,同一个颜色表示使用时是成对出现的。
1.1 Serial 收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC) [ˈsɪriəl]
Serial(串行)收集器是最基本、历史最悠久的垃圾收集器了。看名字就知道这个收集器是一个单线程收集器了。它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( “Stop The World” ),直到它收集结束。
新生代采用复制算法,老年代采用标记-整理算法。
虚拟机的设计者们当然知道Stop The World带来的不良用户体验,所以在后续的垃圾收集器设计中停顿时间在不断缩短(仍然还有停顿,寻找最优秀的垃圾收集器的过程仍然在继续)。
但是Serial收集器有没有优于其他垃圾收集器的地方呢?当然有,它简单而高效(与其他收集器的单线程相比)。Serial收集器由于没有线程交互的开销,自然可以获得很高的单线程收集效率。
重点:Serial Old收集器是Serial收集器的老年代版本,它同样是一个单线程收集器。它主要有两大用途:一种用途是在JDK1.5以及以前的版本中与Parallel Scavenge收集器搭配使用,另一种用途是作为CMS收集器的后备方案。
1.2 Parallel Scavenge收集器(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代)) [ˈpærəlel]
Parallel收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和Serial收集器类似。默认的收集线程数跟cpu核数相同,当然也可以用参数(-XX:ParallelGCThreads)指定收集线程数,但是一般不推荐修改。
Parallel Scavenge收集器关注点是吞吐量(高效率的利用CPU)。CMS等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是CPU中用于运行用户代码的时间与CPU总消耗时间的比值。 Parallel Scavenge收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解的话,可以选择把内存管理优化交给虚拟机去完成也是一个不错的选择。
新生代采用复制算法,老年代采用标记-整理算法。
重点:Parallel Old收集器是Parallel Scavenge收集器的老年代版本。使用多线程和“标记-整理”算法。在注重吞吐量以及CPU资源的场合,都可以优先考虑 Parallel Scavenge收集器和Parallel Old收集器(JDK8默认的新生代和老年代收集器)。
1.3 ParNew收集器(-XX:+UseParNewGC)
ParNew收集器其实跟Parallel收集器很类似,区别主要在于它可以和CMS收集器配合使用。
新生代采用复制算法,老年代采用标记-整理算法。
它是许多运行在Server模式下的虚拟机的首要选择,除了Serial收集器外,只有它能与CMS收集器配合工作。
1.4 CMS收集器(****-XX:+UseConcMarkSweepGC(old))
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用,它是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。
从名字中的Mark Sweep这两个词可以看出,CMS收集器是一种 “标记-清除”算法实现的,它的运作过程相比于前面几种垃圾收集器来说更加复杂一些。整个过程分为四个步骤:
**总结:**从它的名字就可以看出它是一款优秀的垃圾收集器,主要优点:并发收集、低停顿。但是它有下面几个明显的缺点:
对CPU资源敏感(会和服务抢资源);
无法处理浮动垃圾(在并发标记和并发清理阶段又产生垃圾,这种浮动垃圾只能等到下一次gc再清理了);
它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生,当然通过参数-XX:+UseCMSCompactAtFullCollection可以让jvm在执行完标记清除后再做整理
执行过程中的不确定性,会存在上一次垃圾回收还没执行完,然后垃圾回收又被触发的情况,特别是在并发标记和并发清理阶段会出现,一边回收,系统一边运行,也许没回收完就再次触发full gc,也就是"concurrent mode failure",此时会进入stop the world,用serial old垃圾收集器来回收
CMS的相关核心参数
垃圾收集器总结:
Serial收集器主要用于JDK1.5及以前版本,所以这收集器现在使用的应该很少了,缺点是一个单线程收集器;Parallel收集器是JDK1.8默认的收集器,关注点是吞吐量(高效利用CPU-cpu中用于运行用户代码的时间和cpu总消耗时间的比值),适用于内存为2-3G,太大效率不高。CMS收集器更多关注点是用户线程停顿时间(提高用户体验),配置复杂,适用于大内存。ZGC是JDK11版本的,Serial串行->Parallet并行->CMS->G1,G1推荐内存为8G以上到几百G。