垃圾收集器,你知道有哪些吗?

317 阅读6分钟

Serial回收器

串行回收

Serial收集器是最基本、历史悠久的垃圾收集器了,JDK1.3之前回收新生代唯一的选择。Serial收集器作为HotSpotClient模式下的默认新生代垃圾收集器。Serial收集器采用复制算法、串行回收和"Stop-the World"机制的方式执行内存回收。除了年轻代之外,Serial收集器还提供用于执行老年代垃圾收集的Seral Old收集器,Serial Old收集器同样也采用了串行回收和"Stop-the-World"机制,只不过内存回收算法使用的是“标记-压缩(整理)”算法

image.png

这种收集器是一个单线程的收集器,即垃圾收集时只有一个垃圾收集线程在工作,但是它的“单线程”不仅仅说明它只会使用一个CPU或者一条收集线程去完成垃圾收集工作,更重要的是在于它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束(Stop-the-World)。

Serial Old用途

Serial Old是运行在Client模式下默认的老年代垃圾回收器。Serial OldServer模式下主要有两个用途:

  • 与新生代的Parallel Scavenge配合使用
  • 作为老年代CMS收集器的后备来及收集方案。

优点

简单而高效,对于限定单个CPU的环境来说,或者准确来说单核CPU情况下,Serial收集器由于没有线程交互开销,专心做垃圾收集自然可以获得最高的单线程收集效率。在用户的桌面应用场景中可用内存一般不大(几十MB至一两百MB),可以在较短时间内完成垃圾收集(几十ms至一百多ms),只要不频繁发生,使用串行回收器是可以接受的。

总结

这种垃圾收集器大家了解即可,现在已经不用串行的了,而且在限定单核CPU才可以使用,现在都不是单核的了。对于交互较强的应用,这种垃圾收集器是不能接受的,一般在Java Web应用程序中不会采用串行垃圾收集器的。

ParNew

ParNew收集器介绍

如果说Serial GC是年轻代中的单线程垃圾收集器,那么ParNew收集器则是Serial收集器的多线程版本。ParNewParallel的缩写:只能处理的是新生代。ParNew收集器除了采用并行回收的方式回收内存外,两款垃圾收集器之间几乎没有任何区别,ParNew收集器在年轻代中同样使用复制算法、"Stop-the-World"机制。ParNew是很多JVM运行在Server模式下新生代的默认垃圾收集器。

image.png

  • 对于新生代,回收次数频繁,使用并行的方式高效。
  • 对于老年代,回收次数少,使用串行方式节省资源(CPU并行需要切换线程,串行可以节省切换线程的资源)。

由于ParNew收集器是基于并行回收,那么是否可以断定ParNew收集器的回收效率在任何场景下都比Serial收集器更好呢?

  • ParNew收集器运行在多CPU的环境下,由于可以充分利用多CPU、多核心等物理硬件资源优势,可以更快地完成垃圾收集,提升程序地吞吐量。

  • 但是在单个CPU环境下,ParNew收集器不比Serial收集器更高效,虽然Serial收集器是基于串行回收地,但是由于CPU不需要频繁地任务切换,因此可以有效避免县城交互过程中产生地一些额外开销。目前除了Serial外,只有ParNew GC能与CMS收集器配合工作。

ParNew收集器参数设置

在程序中,开发人员可以通过选项-XX:+UseParNewGC手动指定使用ParNew收集器执行内存回收任务,它表示年轻代使用并行收集器,不影响老年代。-XX:ParallelGCThreads限制线程数量,默认开启和CPU数据相同地线程数。

Parallel回收器:吞吐量优先

基本介绍

HotSpot的年轻代中除了拥有ParNew收集器是基于并行回收的以外,Parallel Scavenge收集器同样也采用了复制算法、并行回收和"Stop the World"机制。

那么Parallel 收集器的出现是否多此一举?

  • ParNew收集器不同,ParallelScavenge收集器的目标则是达到一个可控制的吞吐量(Throughput),它也被称为吞吐量优先的垃圾收集器。
  • 自适应调节策略也是Paralle1 ScavengeParNew一个重要区别。

高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。因此,常见在服务器环境中使用。例如,那些执行批量处理、订单处理、工资支付、科学计算的应用程序。

Paralle1收集器在JDK1.6时提供了用于执行老年代垃圾收集的Paralle 1o1d收集器,用来代替老年代的serial old收集器。

Parallel old收集器采用了标记-压缩算法,但同样也是基于并行回收和"stop-the-World"机制。

image.png

在程序吞吐量优先的应用场景中,Paralle1收集器和Parallel old收集器的组合,在server模式下的内存回收性能很不错。在Java8中,默认是此垃圾收集器。

参数配置

  • -XX:+UseParallelGC 手动指定年轻代使用Paralle1并行收集器执行内存回收任务。

  • -XX:+UseParalleloldcc 手动指定老年代都是使用并行回收收集器。分别适用于新生代和老年代。默认jdk8是开启的。上面两个参数,默认开启一个,另一个也会被开启。(互相激活)

  • -XX:ParallelGcrhreads:设置年轻代并行收集器的线程数。一般地,最好与CPU数量相等,以避免过多的线程数影响垃圾收集性能。在默认情况下,当CPU数量小于8个,ParallelGcThreads的值等于CPU数量。当CPU数量大于8个,ParallelGCThreads的值等于3+[5*CPU Count]/8]

  • -XX:MaxGCPauseMillis: 设置垃圾收集器最大停顿时间(即STw的时间)。单位是毫秒。为了尽可能地把停顿时间控制在MaxGCPauseMi11s以内,收集器在工作时会调整Java堆大小或者其他一些参数。 对于用户来讲,停顿时间越短体验越好。但是在服务器端,我们注重高并发,整体的吞吐量。所以服务器端适合Parallel,进行控制。该参数使用需谨慎。

  • -XX:GCTimeRatio:垃圾收集时间占总时间的比例(=1/(N+1))。用于衡量吞吐量的大小。取值范围(0,100)。默认值99,也就是垃圾回收时间不超过1。与前一个-xx:MaxGCPauseMillis参数有一定矛盾性。暂停时间越长,Radio参数就容易超过设定的比例。

  • -XX:+UseAdaptivesizepplicy: 设置Parallel scavenge收集器具有自适应调节策略。在这种模式下,年轻代的大小、EdenSurvivor的比例、晋升老年代的对象年龄等参数会被自动调整,已达到在堆大小、吞吐量和停顿时间之间的平衡点。在手动调优比较困难的场合,可以直接使用这种自适应的方式,仅指定虚拟机的最大堆、目标的吞吐量(GCTimeRatio)和停顿时间(MaxGCPauseMil1s),让虚拟机自己完成调优工作。