本文默认JVM为HotSpot。以前已经介绍过常见的垃圾收集算法,此次再来讲说JVM具体实现了这些算法的垃圾收集器。算法
下图为HotSpot虚拟机有的垃圾收集器,有连线关系的表示能够搭配使用,反之不能。 服务器
此垃圾收集器年代久远,用于新生代的垃圾收集,采用复制算法。是单线程的垃圾收集器也就是无论你的服务器有多少CPU,反正它就用其中的一个CPU启动一个线程去处理垃圾回收,而且中止全部工做线程等待它回收完成。因此它在收集时会STW(stop the world)。能与其搭配的老年代收集器是CMS与Serial Old。 多线程
此垃圾收集器能够说是Serial的多线程版本,它和Serial的差异就在于复制的时候是多线程的。 并发
Scavenge是捡破烂的意思...恩并行捡破烂说的是好像没错,用的也是复制算法。那不是已经在ParNew了吗,怎么还来个并行的。它和ParNew主要有两个不一样点框架
一、Parallel Scavenge的关注点在于可控制的吞吐量,吞吐量=运行用户代码的时间/(运行用户代码的时间+GC时间)。就是说它的重点不在于想缩短每次GC的时间,而在于控制虚拟机运行一段时间中,所花费在GC上的总时间。好比程序运行了100分钟,其间垃圾收集了1分钟,那吞吐量就是99%。post
二、Parallel Scavenge能自适应调节新生代中配置的参数,例如Eden和survivor比例等。其实就是由于它能自适应,因此才能可控制吞吐量,它根据实际状况动态调整这些参数来达到要求的吞吐量。性能
此收集器也提供了“-XX:MaxGCPauseMillis”控制垃圾收集最大停顿时间(容许值大于0),“-XX:GCTimeRatio”吞吐量(1-99)。 看到“-XX:MaxGCPauseMillis”,别觉得咱们想设置多少就多少,收集器只能尽量的保证而已。并且说白了能若是想提升新生代GC的速度,那就是减小新生代的内存空间,内存空间少垃圾确定少处理起来确定快。可是空间少是否是更快的容易满啊,因此所需的GC次数确定会增多,那吞吐量也会降低。线程
好比说一个程序如今跑在服务器上,假设每次新生代GC时间是100毫秒,每10秒钟一次新生代GC,那一分钟花费在GC上的时间就是600毫秒。那我想每次花在GC时间更少好比60毫秒,那就减小新生代内存空间,可是这样每5秒钟一次GC,那一分钟花费在GC上的时间就是720毫秒。3d
对应使用的场景就是若是你的服务是计算类的,默默在后台计算,和用户交互不多,因此你确定想的是吞吐量大,也就是总的GC时间短,能充分的用了CPU来计算,这个时候就适合用Parallel Scavenge。cdn
那若是你的程序是交互类的,你的要求确定就是STW的时间越短越好,能快速响应客户的请求。Parallel Scavenge也行,可是它不能和CMS联合使用呀!由于Parallel Scavenge没有使用本来HotSpot中和其它GC通用的那个GC框架,而是新框架。因此默认和CMS搭配的就是ParNew。
它是Serial 收集器的老年代版本,是单线程收集,采用的是标记-整理算法。主要用于client模式和CMS的后备收集器。除了G1,上面说的几个新生代收集器均可以与它搭配使用。图请参考上面Serial。
它是Parallel Scavenge的老年代版本,是多线程收集,采用的是标记-整理算法。它只能和Parallel Scavenge搭配。它的出现打破了Parallel Scavenge尴尬的地位,由于以前Parallel Scavenge只能和Serial Old配合,人家新生代都多线程跑了,奈何老年代只有单线程,拖累它了。图请参考上面Parallel Scavenge。
CMS(Concurrent Mark Sweep),从名字能够看出它采用的是标记-清除算法。它致力于减小STW的时间,让垃圾收集时同时用户线程也能并行着。在目前的Server主流垃圾收集器。
一、初始标记(会STW)
二、并发标记
三、从新标记(会STW)
四、并发清理
初始标记就是仅标记GC Roots直接关联的对象,不继续深刻标记,致力于减小STW时间。并发标记就是深刻标记遍历后面全部关联对象。从新标记就是修正因并发标记阶段而发生变更了的对象标记会STW。而后就是并发清理垃圾。
因此CMS把所需消耗时间最长的深刻标记阶段和清理阶段与用户线程并行。大大减小了STW所需的时间。
可是它有如下3个缺点:
一、并发阶段会与工做线程争抢CPU资源
二、空间碎片问题,由于采起的是标记-清除算法因此会产生空间碎片。为何解决这个问题CMS提供了"-XX:+UseCMSCompactAtFullCollection"(默认开启),用于当CMS顶不住须要进行FullGC时整理空间碎片,可是整理的过程是用户线程是得中止工做的,因此停顿的时间会变长。
三、浮动垃圾问题。由于在并发清理的时候容许用户线程继续执行,而执行就可能产生新的垃圾进入老年代,因此须要预留一部分空间给这些浮动垃圾,而当这些浮动垃圾过多在CMS运行期间爆了,那CMS就会出现“Concurrent Mode Failure”,这是时候就得后备的Serial Old上来从新进行老年代的垃圾收集,因此停顿的时间就更长了。
此垃圾收集器不须要和别人配合,本身处理新生代和老年代。在jdk9中G1变为Server模式默认的垃圾收集器。它的发明就是为了替代CMS。
G1(Garbage-First)从总体来看是基于标记-整理的算法,从局部来看是基于复制算法。它和CMS同样能够和用户进程并行。相对于CMS 它的优势是首先它能创建可预测的停顿时间模型,能在一个规定的时间段内指定垃圾收集的时间不超过限制的毫秒数,而且它将Java堆分为多个大小相等的独立区域,也就是Region。虽然它还保留着分代的概念,可是新生代和老年代不是物理隔离了。 它的清理区间再也不是整个新生代或者老年代,而是以区域为划分,不会产生空间碎片。
G1会维护一个优先列表,根据跟踪各个region回收所能产生的空间大小和时间来标定优先级,优先回收优先级最大的Region。这就等于每次的回收目标更加精确化,提升回收的效率 G1的收集步骤可分为:
一、初始标记
二、并发标记
三、最终标记
四、筛选回收
初始标记和CMS同样先标记GC Roots直接关联对象,而后并发深刻标记,遍历关联对象。最终标记和CMS从新标记一个概念,筛选回收也就是筛选下决定回收哪一个Region价值更大。
若是有错欢迎指正!我的公众号:yes的练级攻略