前言:
目前已经发展到jdk11了。很多资料上的垃圾收集器还停留在1.7以前。本文基于收集器的发展路线,从前到后汇总和简单分析一下JVM垃圾收集器的roadmap。本文暂且从对内存区管理和回收特色方面分为分代收集和非分代两个part。
Part I、分代收集阶段
一、新生代收集器
1.Serial New
这是最早的新生代收集器,也是jdk1.5之前默认的收集器,在GC log里可以经常看到[DefNew 的字样,说的就是这个收集器。它是基于复制算法(算法不在本文描述范畴)实现的,单线程,而且需要stop the world,所以新生代不能太大,否则对于停顿来讲是比较影响交互响应的。
2.Parallel New
这是对单线程的Serial的一种改进,ParNew收集器是并行的,在多CPU的场景下会有比串行收集器更好的性能,除此之外,实现算法跟Serial完全一样。这种收集器在采用CMS(后文会讲到,一种老年代收集器)时,默认新生代会采用ParNew收集器。需要注意的是,如果CPU数量为1个或者少于4个时,该种收集器的性能并不会比Serial要好。因为除去上下文切换,以及占用用户线程CPU时间片,导致用户线程被拖慢。
3.Parallel Scavenge
这也是一种新生代垃圾收集器,PSYoungGen它采用的也是复制算法,它与前两种收集器最大的区别是,它关注的是吞吐量而不是延迟。也被称为是吞吐量优先的收集器。其中,吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。主要使用场景:主要适合在后台运算而不是太多交互的任务,高吞吐量则可以最高效率的利用CPU时间,尽快的完成程序的运算任务。当然,如果想要降低停顿时间,相应的也会影响吞吐量。几个重要的参数:
- 控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数
MaxGCPauseMillis参数允许的值是一个大于0的毫秒数,收集器将尽力保证内存回收花费的时间不超过设定值。不过大家不要异想天开地认为如果把这个参数的值设置得稍小一点就能使得系统的垃圾收集速度变得更快,GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的:系统把新生代调小一些,收集300MB新生代肯定比收集500MB快吧,这也直接导致垃圾收集发生得更频繁一些,原来10秒收集一次、每次停顿100毫秒,现在变成5秒收集一次、每次停顿70毫秒。停顿时间的确在下降,但吞吐量也降下来了。
- 设置吞吐量大小的 -XX:GCTimeRatio参数。
GCTimeRatio参数的值应当是一个大于0小于100的整数,也就是垃圾收集时间占总时间的比率,相当于是吞吐量的倒数。如果把此参数设置为19,那允许的最大GC时间就占总时间的5%(即1 /(1+19)),默认值为99,就是允许最大1%(即1 /(1+99))的垃圾收集时间。
- 自适应策略开关UseAdaptiveSizePolicy参数。
-XX:+UseAdaptiveSizePolicy是一个开关参数,当这个参数打开之后,就不需要手工指定新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象年龄(-XX:PretenureSizeThreshold)等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomics)
在实现上比前两种改进了很多,但是一直到1.6以后才真正用起来,这是因为,它不能跟CMS收集器一起配合工作。在此之前,使用该种新生代收集器的话,老年代收集器必须使用Serial Old收集器。