前文学习了各类GC回收算法,掌握了GC回收的原理,可是真正的GC实现却尤其复杂,本篇文章将主要介绍各类GC收集器。算法
目前主流的HotSpot VM支持多种虚拟机,这些虚拟机也体现了GC的发展历程,从单线程GC到多线程GC,分代GC到G1 GC。缓存
本文主要从如下几个方面介绍GC收集器:多线程
HotSpot中采用分代GC,从早期的单线程串行Garbage Collector到后面的多线程并行Garbage Collecot,衍生出了不少款Collector。并发
其中负责收集年轻代:oracle
其中负责收集老年代:框架
Serial收集器是一款很是古老的收集器,它使用单线程串行方式回收年轻代,会产生STW。post
Note:
STW即Stop The World,即中止全部用户线程,只有GC线程在运行。学习
每次进行GC时,首先停顿全部的用户线程,而后只有一个GC线程回收年轻代中的死亡对象。在Java Client模式中,默认任然使用Serial,由于Client模式主要针对桌面应用,通常内存较小,在百M范围内,使用单线程收集Serial效率很是高,能够带来不多时间的停顿,用户体检很是好。spa
在早期,只有单线程收集器时,年轻代别无选择。后续又演变成多线程GC年轻代,便衍生出ParNew这款并行收集器,它的并行实现主要是在GC期间使用多线程回收年轻代。线程
这款并行收集器在GC期间,也须要STW。通常多数用于Server端的年轻代GC。
顾名思义,这个款年轻代收集器也是并行收集器,和ParNew的功能差很少,一样适用复制算法。可是它更注重系统运行的吞吐量。这里说的吞吐量,指的是CPU用于运行应用程序的时间和CPU总时间的占比,吞吐量 = 代码运行时间 / (代码运行时间 + 垃圾收集时间)。可是它的来源比较奇葩,没有遵循GC框架,致使和CMS不能兼容。关于这点能够参考:
ParNew 和 PSYoungGen 和 DefNew 是一个东西么?
该收集器和Serial收集器的功能同样,都是单线程串行收集器,GC期间也会STW。可是它用于收集老年代且使用了标记整理算法,这两点它和Serial收集器不同。主要也是应用在Client模式下的桌面应用中。
Parallel Old和ParNew和Parallel Scavenge相似,是一款老年代的多线程并行收集器。通常只配合Parallel Scavenge使用。
CMS(Concurrent Mark Sweep 并发标记清理)收集器是平常应用中最常被使用的收集器。它主要是为了减小停顿的时间,下降延迟而生,多应用对实时性要求比较的应用场景,如:互联网应用。
它主要分为四个过程:
其中初始标记和从新标记仍然会STW暂停用户线程,可是这两个过程的停顿时间相对于并发标记和并发清除而言相对较短,而并发标记和并发清除阶段GC线程则能够和用户线程并发运行。
因为CMS收集器一样使用标记清除算法,因此存在内存碎片问题,从而可能形成大对象没法分配发生提早GC。因此CMS收集器又提供了参数控制其进行内存碎片整理,默认是开启状态,这个过程是很是长的。
从以上内容介绍,能够看出分代GC分为不少种,随着演化过程,每种都有各自的应用场景。从其收集特色上能够分为三类:
虽然这些分代收集器种类繁多,可是他们之间有相互匹配,并不是任意使用。配对的使用状况以及参数见下表:
young | old | 参数 |
---|---|---|
Serial | Serial old | -XX:+UseSerialGC |
ParNew | Serial old | -XX:+UseParNewGC |
Parallel Scavenge | Serial old | -XX:+UseParallelGC |
Parallel Scavenge | Parallel Old | -XX:+UseParallelOldGC |
ParNew | CMS + Serial Old | -XX:+UseConcMarkSweepGC |
Serial | CMS | -XX:+UseConcMarkSweepGC -XX:-UseParNewGC |
GC收集器众多,每种GC收集器的GC日志格式都不同。这里笔者作了下总结,一样是针对以上的各类匹配状况作了日志格式的收集。
0.299: [GC (Allocation Failure) 0.299: [DefNew: 1770K->428K(4928K), 0.0019955 secs] 9962K->8620K(15872K), 0.0020405 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 0.304: [GC (Allocation Failure) 0.304: [DefNew (promotion failed) : 4612K->4193K(4928K), 0.0014249 secs]0.305: [Tenured: 8588K->4499K(10944K), 0.0034912 secs] 12804K->4499K(15872K), [Metaspace: 3094K->3094K(1056768K)], 0.0049774 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
使用Serial + Serial Old搭配式,年轻代收集将会是DefNew,老年代将会是Tenured。
0.243: [GC (Allocation Failure) 0.243: [ParNew: 1770K->470K(4928K), 0.0029402 secs] 9962K->8662K(15872K), 0.0029933 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 0.251: [GC (Allocation Failure) 0.251: [ParNew (promotion failed): 4566K->4096K(4928K), 0.0009985 secs]0.252: [Tenured: 8632K->4516K(10944K), 0.0025514 secs] 12758K->4516K(15872K), [Metaspace: 3093K->3093K(1056768K)], 0.0035906 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
使用Serial + Serial Old搭配式,年轻代收集将会是ParNew,老年代将会是Tenured。
0.224: [GC (Allocation Failure) [PSYoungGen: 0K->0K(4608K)] 8619K->8619K(15872K), 0.0004876 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 0.225: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(4608K)] [ParOldGen: 8619K->8601K(11264K)] 8619K->8601K(15872K), [Metaspace: 3080K->3080K(1056768K)], 0.0046498 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
使用Parallel Scavenge + Parallel Old搭配式,年轻代收集将会是PSYoungGen,老年代将会是ParOldGen。
0.351: [GC (Allocation Failure) 0.351: [ParNew: 1769K->446K(4928K), 0.0008713 secs] 9961K->8638K(15872K), 0.0009216 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 0.354: [GC (CMS Initial Mark) [1 CMS-initial-mark: 8192K(10944K)] 12734K(15872K), 0.0004513 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 0.355: [CMS-concurrent-mark-start] 0.355: [CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 0.355: [GC (Allocation Failure) 0.355: [ParNew (promotion failed): 4631K->4267K(4928K), 0.0007242 secs]0.356: [CMS (concurrent mode failure): 8207K->4500K(10944K), 0.0031171 secs] 12823K->4500K(15872K), [Metaspace: 3080K->3080K(1056768K)], 0.0038915 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
使用CMS收集器的GC日志格式很是明显,有CMS的GC过程,年轻代将使用ParNew标志。