16. 垃圾回收器
16.1 GC分类与性能指标
垃圾回收器的分类:
· 按线程数分,可以分为串行垃圾回收器和并行垃圾回收器
>串行回收器指的是在同一时间段内只允许有一个CPU用于执行垃圾回收操作,此时工作线程被暂停,直至垃圾收集
工作结束
>在诸如单CPU处理器或者较小的应用内存等硬件平台不是特别优越的场合,串行回收器的性能表现可以超过并行回收
器和并发回收器,所以,串行回收器默认被应用在客户端的Client模式下的JVM中
>在并发能力比较强的CPU上,并行回收器产生的停顿时间要短于串行回收器
· 和串行回收相反,并行收集可以运用多个CPU同时执行垃圾回收。因此提升了应用的吞吐量,不过并行回收仍然与串行
回收一样,采用独占式,使用了“Stop-the-world”机制
· 按照工作模式分,可分为并发式垃圾回收器和独占式垃圾回收器
>并发式垃圾回收器与应用程序线程交替工作,以尽可能减少程序停顿时间
>独占式垃圾回收器(Stop the world)一旦运行,就停止应用程序中的所有用户线程,直到垃圾回收完全结束
· 按照碎片处理方式分,可分为压缩式垃圾回收器和非压缩式垃圾回收器
>压缩式垃圾回收器会在回收完成后,对存活对象进行压缩整理,消除回收后的碎片
>非压缩式的垃圾回收器不进行这步操作
· 按照工作的内存区间分,又可分为年轻代垃圾回收器和老年代垃圾回收器
评估垃圾回收器的性能指标:
· 吞吐量:运行用户代码的时间占总运行时间的比例(总运行时间:程序的运行时间 + 内存回收的时间)
· 垃圾收集开销:吞吐量的补数,垃圾收集所用时间与总运行时间的比例
· 暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间
· 收集频率:相较于应用程序的执行,收集操作发生的频率
· 内存占用:Java堆区所占用的内存大小
· 快速:一个对象从诞生到被回收所经历的时间
16.2 不同垃圾回收器概述
七款经典垃圾回收器分类:
· 串行回收器:Serial、Serial Old
· 并行回收器:ParNew、Parallel Scavenge、Parallel Old
· 并发回收器:CMS、G1
垃圾回收器发展史:
七款经典收集器与垃圾分代之间的关系:
· 新生代收集器:Serial、ParNew、Parallel Scavenge
· 老年代收集器:Serial Old、Parallel Old、CMS
· 整堆收集器:G1
垃圾回收器的组合关系:
如何查看默认的垃圾收集器:
· -XX: + PrintCommandLineFlag : 查看命令行相关参数(包含使用的垃圾收集器)
· 使用命令行指令:jinfo -flag相关垃圾回收器参数 进程ID
16.3 Serial回收器:串行回收
串行回收概述:
· Serial收集器是最基本,历史最悠久的垃圾收集器。JDK1.3之前回收新生代唯一的选择
· Serial收集器作为HotSpot中Client模式下的默认新生代垃圾收集器
· Serial收集器采用复制算法、串行回收和Stop-the-world机制的方式执行内存回收
· 除了年轻代以外,Serial收集器还提供用于执行老年代垃圾收集的Serial Old收集器。Serial Old收集器同样采用了串行
回收和Stop-the-world机制,只不过内存回收算法使用的是标记-压缩算法
>Serial Old是运行在Client模式下默认的老年代的垃圾回收器
>Serial Old在Server模式下主要有两种用途:①与新生代的Parallel Scavenge配合使用 ②作为老年代CMS后备垃圾收集
串行回收图示:
· 这个收集器是一个单线程的收集器,但它的"单线程"的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成
垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束(Stop-The-World)
Serial回收器的优势:
· 优势:简单而高效(与其他收集器的单线程相比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销
专心做垃圾收集自然可以获得最高的单线程收集效率
>运行在Client模式下的虚拟机是个不错的选择
· 在用户的桌面应用场景中,可用内存一般不大(几十Mb至一两百Mb),可以在较短的时间内完成垃圾收集(几十ms左右),只
要不频繁发生,使用串行回收器是可以接受的
· 在HotSpot虚拟机中,使用 -XX :+ UseSerialGC参数可以指定年轻代和老年代都使用串行收集器
>等价于新生代用Serial GC,且老年代用Serial Old GC
16.4 ParNew回收器:并行回收
ParNew并行回收概述:
· 如果说Serial GC是年轻代中的单线程垃圾收集器,那么ParNew收集器则是Serial收集器的多线程版本
>Par是Parallel的缩写,New : 只能处理的是新生代
· ParNew收集器除了采用并行回收的方式执行内存回收外,两款垃圾收集器之间几乎没有任何区别。ParNew收集器在年轻
代中同样也采用复制算法,"Stop-the-World"机制
· ParNew是很多JVM运行在Server模式下新生代的默认垃圾收集器
ParNew并行回收图示:
· 对于新生代,回收次数比较频繁,使用并行方式比较高效
· 对于老年代,回收次数比较少,使用串行方式节约资源(CPU并行需要切换线程,串行可以省去切换线程的资源)
· 在程序开发过程中,使用 -XX :+ UseParNewGC手动指定使用ParNew收集器执行内存回收任务,它表示年轻代使用并行
收集器,不影响老年代
· -XX :ParallelGCThread限制线程数,默认开启和CPU数据相同的线程数
16.5 Parallel回收器:吞吐量优先
Parallel Scavenge回收器概述:
· HotSpot的年轻代中除了拥有ParNew收集器是基于并行回收以外,Parallel Scavenge收集器同样也采用复制算法,并行
回收和"Stop-the-World"机制
· 那么Parallel收集器的出现是否多次一举?
>和ParNew收集器不同,Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput),它也被称为吞吐量
优先的垃圾收集器
>自适应调节策略也是Parallel Scavenge与ParNew一个重要区别
· 高吞吐量则可以高效利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。因此
常见在服务器环境中使用,例如:哪些执行批量处理,订单处理,工资支付,科学计算的应用程序
· Parallel收集器在JDK1.6时提供了用于执行老年代垃圾收集的Parallel Old收集器,用来替代老年代的Serial Old收集器
· Parallel Old收集器采用了标记-压缩算法,但同样也是基于并行回收和"Stop-the-World"机制
· Parallel收集器和Parallel Old收集器组合,在Server模式下的内存回收性能很不错,Java8默认是次垃圾收集器。
16.6 CMS回收器:低延迟
CMS回收器概述:
· 在JDK1.5时期,HotSpot推出了一款在强交互应用中几乎可以认为有划时代意义的垃圾收集器(Concurrent-Mark- Sweep)
收集器,这款收集器是HotSpot虚拟机中第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程同
时工作
· CMS收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间,停顿时间短(低延迟)就越适合与用户交互的程序,良
好的响应速度能够提升用户体验
>目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停
顿时间最短,以给用户带来较好的体验,CMS收集器就非常符合这类应用需求
· CMS的垃圾收集算法采用标记-清除算法,并且也会"Stop-the-World"
· 不幸的是CMS作为老年代的收集器,无法与JDK1.4中已存在的新生代收集器Parallel Scavenge配合工作,所以在JDK1.5中
使用CMS来收集老年代的时候,新生代只能选择ParNew或者Serial收集器中的一个
· 在G1出现之前,CMS使用还是非常广泛,一直到今天,任然有很多系统使用CMS GC
CMS工作原理:
· CMS整个过程比之前的收集器要复杂,整个过程分为4个主要阶段,标识阶段、并发标记阶段、重新标记阶段、并发清除
>初始标记(Initial-Mark)阶段:在这个阶段中,程序中所有的工作线程都将会因为"Stop-the-World"机制而出现短暂的暂
停,这个阶段的主要任务仅仅是标记出GC Roots能直接关联的对象。一旦标记完成之后就会恢复之前被暂停的所有应
用线程。由于直接关联对象比较小,所以这里的速度非常快
>并发阶段(Concurrent-Mark)阶段:从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时比较长但是
不需要停顿用户线程,可以和垃圾收集线程一起并发运行。
>重新标记(Remark)阶段:由于在并发标记阶段中,程序的工作线程与垃圾收集线程同时运行或者交叉运行,因此为了
修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常
会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短
>并发消除(Concurrent-Sweep)阶段:次阶段清理删除掉标记阶段判断的已经死亡的对象,释放内存空间,由于不需要
移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
16.7 G1回收器:区域化分代式
G1回收器的特点:
· 并行与并发:
>并行性:G1在回收期间,可以有多个GC线程同时工作,有效利用多核计算能力。此时用户线程STW
>并发性:G1拥有与应用程序交替执行的能力,部分工作和应用程序同时执行,因此一般来说,不会在整个回收阶段发
生完全阻塞应用程序的情况
· 分代收集:
>从分代上看,G1依然属于分代型垃圾回收器,它会区分年强代和老年代,年强代依然有Eden区和Survivor区。但是从
堆的结构上看,它不要求整个Eden区,年轻代,或者老年代都是连续的,也不再坚持固定大小和固定数量
>将堆空间分为若干区域(Region),这些区域中包含了逻辑上的年强代和老年代
>和之前各类回收器不同,它同时兼顾年强代和老年代,对比其他回收器,或者工作在年轻代,或者工作在老年代
· 空间整合:
>CMS:"标记-清除"算法、内存碎片、若干次GC后进行一次碎片清理
>G1将内存划分为一个个的Region。内存回收是以Region作为基本单位的。Region之间是复制算法,但是整体上实际可
看作是标记-压缩(Mark-Compact)算法,两种算法都可以避免内存碎片。这种特性有利于程序长时间运行,分配大对象
时不会因为无法找到连续空间而提前触发下一次GC。尤其是当Java堆非常大的时候,G1的优势更加明显
· 可预测的停顿时间模型:
>由于分区的原因,G1可以只选取部分区域进行内存回收,这样缩小了回收的范围,因此对于全局停顿情况的发生也能
得到较好的控制
>G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个
优先列表,每次根据允许的收集时间,优先回收价值最大的Region。保证了G1收集器在有限时间内可以获取尽可能高
的收集效率
>相比于CMS GC,G1未必能做到CMS在最好情况下的延时停顿,但是最差情况要好很多
G1结构图:
G1垃圾回收的过程:
G1 GC的垃圾回收过程主要包括三个环节
· 年轻代GC(Young GC)
· 老年代并发标记过程(Concurrent Marking)
· 混合回收(Mixed GC)
· (如果需要,单线程、独占式、高强度的Full GC还是继续存在的,它对GC评估的失败提供了失败保护机制,即强力回收)
16.8 垃圾回收器总结
截止JDK1.8,一共有7种不同的垃圾收集器,每一款不同的垃圾收集器都有不同的特点,在具体使用的时候,需要根据具体的情况选用不同的垃圾收集器
16.9 GC日志分析