JVM Throughput和CMS垃圾回收器

垃圾收集器

Throughtput 垃圾收集器

简介

Throughtput收集器是一款关注吞吐量的收集器。这个收集器也是唯一一个实现了UseAdaptiveSizePolicy策略的垃圾收集器。允许用户通过指定最大的暂停时间和垃圾收集器所占用的时间百分比,然后动态调整JVM的参数来达到配置的目标。

垃圾收集器

上图是一个经典的垃圾收集器的图,图中有颜色的就是 throughput垃圾回收器 ,各个线代表的是各个垃圾回收器之间哪两个可以配合使用。

从上图可以看出 Parralel Scanvenge垃圾回收器不能和CMS配合使用。

ParallelScavenge收集器

ParallelScavenge收集器是新生代收集器,使用的是Scavenge GC。是一个并行收集器。在copping阶段可以多个线程一起执行,在多线程的场景下可一尽量的提高Minor GC的效率。目的是可以达到一个可控的吞吐量。

吞吐量 = 运行用户代码的时间/(运行用户代码时间+垃圾收集时间)

虚拟机运行总时间100分钟,其中垃圾收集器运行了1分钟,则吞吐量就是99%

ParallelScavenge收集器的来历为,在HotSpot VM开发的时候都是在分代式框架下开发,并且希望第三方开发者也能在这个框架下开发自定义的收集器,这样就能和其他收集器配合使用。但是有个开发者不愿意使用这个框架,并且凭借自己的能力开发了并行收集器,这时候JVM中的并行收集器是不存在的。经过测试,这个收集器的性能还是非常不错的,于是就被放入到了HotSopt中成为了ParallelScavenge收集器。这就是为什么这个收集器不能和CMS配合使用,因为它就不是在分代式框架下开发的。

 

ParallelScavenge是在jdk1.7之前的默认垃圾回收器。使用ParallelScavence收集器需要需要关注以下两个参数:

扫描二维码关注公众号,回复: 4305957 查看本文章
  1. MaxGCPauseMills

这个参数控制GC最大的停顿时间。设置以后通过动态调整新生代的大小来达到暂停时间是可以控制的。但是这个值不是越小越好。因为这个如果这个值设置的比较小,那必定会导致新生代的比较小,新生代空间比较小的话,会导致更加频繁的minor GC,这样总间隔时间就会变大。

  1. GCTimeRatio

收集器运行时间所占时间的比率,介于0到100的整数。

通过调整这两个参数的大小能起到动态调整吞吐量和暂停时间的目的,这样用户不用关心新老年代应该各自设置多大,只需要设置好这两个值,剩下的交给虚拟机动态调整。

Parallel Old

       Parallel Old是ParallelScavenge的老年代版本,之前如果新生代使用ParallelScavenge,老年代只能使用Serial Old,为了弥补这个缺失,所以在jdk 1.6的时候开发了Parallel Old 老年代并行垃圾回收器,配合ParallelScavenge使用。此时两者双剑合璧,才更能显现出来Throughtput的强悍之处

Serial Old收集器

       单线程老年代收集器,主要用在client模式下,不过也作为CMS垃圾收集器并发模式失效以后备用收集器。

Throughtput收集器配置

Throughtput收集器通过第一节的图中可以看出有两种配置

  1. ParallelScavenge+Parallel Old
-XX:+UseParallelGC 

或者

 -XX:+UseParallelOldGC

,这两个配置任选一个,就会选中ParallelScavenge+Parallel Old组合作为收集器。

打印的GC日志如下

 [Full GC (System.gc()) [PSYoungGen: 992K->0K(29696K)] [ParOldGen: 8K->761K(68608K)] 1000K->761K(98304K), [Metaspace: 3152K->3152K(1056768K)], 0.0060131 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
  1. ParallelScavenge+Serial Old

如果非得强制使用单线程老年代收集器(Serial Old)可以如下配置:

-XX:+UseParallelGC -XX:-UserParallelOldGC

发生GC时,打印的日志如下

[Full GC (System.gc()) [PSYoungGen: 928K->0K(29696K)] [PSOldGen: 8K->767K(68608K)] 936K->767K(98304K), [Metaspace: 3167K->3167K(1056768K)], 0.0023446 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

ParallelScavenge和ParNew

在收集器的图中,我们看到还有一个ParNew的新生代收集器,由于CMS收集器不能配合ParallelScavenge使用,所以只剩下一个单线程的Serial收集器,为了能够有所匹配,所以出来了并行的新生代收集器,它和ParallelScavenge相比有如下不同:

  1. 算法不同,ParNew采用的是广度优先遍历对象,而ParallelScavenge采用的是深度优先。
  2. 没有UseAdaptiveSizePolicy策略

 

总结

如果是关注吞吐量的应用,采用Throughput收集器是个不错的选择。并且可以通过设置

-XX:ParallelGCThreads=N 来设置并行GC线程数。在使用的过程中最好不要手工的指定新生代和老年代的大小,而是指定MaxGCPauseMills 和GCTimeRatio让其自动调整,以达到最优值。同时设置我们堆大小即可。

CMS收集器

CMS收集器设计的初衷是了消除Throughtput收集器和Serial收集器FullGC周期的长时间停顿。CMD收集器在Minor GC时会暂停所有应用线程,并且以多线程的方式进行垃圾回收。

CMS收集器在FullGC时不再暂停应用线程,而是使用若干个后台线程定期的对老年代空间进行扫描,及时会后其中不再使用的对象。这种做法使得CMS成为一个低延迟的收集器。应用线程只在Minor GC以及后台线程骚烤老年代时发生极其短暂的停顿。应用程序线程停顿的总时长与使用Throughtput收集器比起来短的多。

但是会付出额外的代价,那就是更高的CPU使用:必须有足够的CPU资源用于后台运行垃圾收集线程。除此之外,后台不再进行压缩整理工作,也就是说堆回逐渐的碎片化。如果CMS的后台线程无法获得完成他们任务所需要的CPU资源,或者如果堆变的过度碎片化以至于无法找到连续内存分配先对象,CMS就会蜕化为Serial收集器的行为,暂停所有应用线程,使用单线程进行回收,整理老年代空间,之后又恢复到并发运行,再次启动后台线程,直到堆变的再次过度碎片化。

开启标志:

-XX:+UseConcMarkSweepGC ,-XX:+UseParNewGC

这两个标志可以开启CMS垃圾回收器。

 

Throughput和CMS的选择

  1. 衡量标准时相应时间或吞吐量,在Throughput收集器和CMS收集器之间做选择的依据主要是有多少空闲CPU资源能用于运行后台的并发线程。
  2. 通常情况下,Throughtput收集器的平均响应时间比Concurrent收集器要差,但是在90%影响时间或者99%响应时间的这几个指标上,Throughput收集器要比Concurrent要好些
  3. 使用Throughput收集器会超负荷进行大量的Full GC时,切换到CMS收集器通常能获得更低的响应时间

猜你喜欢

转载自my.oschina.net/u/2457218/blog/1929682