JVM调优那些事(三)常见的垃圾回收器

前言

在本系列的前两篇文章,已经讲过了内存的垃圾回收方法以及内存的分代模型,但真正把它们整合起来,做具体垃圾回收工作的,还是垃圾回收器

随着工业界内存的不断扩大,垃圾回收器也已经更新了好几个世代。

本文就会对市面上常见的几个垃圾回收器进行介绍,

常用的垃圾回收器介绍

首先来看这张图

在这里插入图片描述
图中一共有八种垃圾回收器。

除了G1 和 ZGC以外,其他的垃圾回收器都是基于内存分代模型的。

有些垃圾回收器是在YGC(Young Garbage Collection)中被触发,而有一些垃圾回收器是在FGC(Full Garbage Collection)的时候被触发。

这样就会出现两种垃圾回收器组合使用的情况。

下面就会先介绍常用的垃圾回收器组合。

Serial + Serial Old

Serial与Serial Old垃圾回收器在JDK诞生之初就追随出现了。

其工作流程如图所示:

在这里插入图片描述

首先需要明确的一点是,垃圾回收工作需要单独起一个线程。

Serial和Serial Old除了在工作区域上不同以外,其他工作机制是相同的——

都是在进行垃圾回收的时候,将所有的工作线程暂停,创建一个单独的垃圾回收线程进行垃圾清理。

将工作线程暂停有个专用名词,叫STW(Stop The World),所有的工作线程为垃圾回收线程让路。

同时Serial Old底层是使用mark-compact算法进行内存回收。

Parallel Scavenge + Parallel Old

在JDK 1.8之前, JVM就默认使用这两种垃圾回收器的组合。

Serial 和 Serial Old垃圾回收器的弊端肉眼可见,单线程的垃圾回收效率低下。

所以Parallel Scavenge + Parallel Old的组合将单线程改成了多线程,不过仍然需要STW。

工作流程如图所示:

在这里插入图片描述
注意,Paralle Old使用了本系列第一篇文章中介绍的mark-compact垃圾回收算法。

ParNew + CMS

看起来Parallel Scavenge + Parallel Old的组合已经很完美了,但还是有明显可以改进的地方,比如,每次垃圾回收的STW,明显会拖慢JVM的运行速度。

为了解决垃圾回收过程的STW,CMS应运而生。

CMS,全称Concurrent Mark Sweep。这是一种可以让垃圾回收线程与工作线程同时运行的技术。

而ParNew只是Parallel Scavenge的一种改进版本,为了更好的和CMS进行协同运作。

下面我们来仔细讲讲这个CMS。

CMS

CMS一次运行周期有四个阶段,分别是初始标记并发标记重新标记并发清理

用图来表示就像下图这样:

在这里插入图片描述
需要指出的是,CMS并没有完全消除掉STW,分别在初始标记阶段和重新标记阶段还会发生STW,不过相比于Parallel Old,CMS已经将最耗时的操作与工作线程并发执行了,从而大大缩短了STW的时间。

下面我们来讲讲一下每一步都具体干了些什么:

初始标记: CMS会把所有的根对象都标记出来,由于只标记根对象,所有STW的时间非常短。

并发标记:这一步是与工作线程并发执行。从上一步标记出来的根对象开始,标记出整个程序的对象图。这个过程虽然耗时较长,但由于是与工作线程并发执行的,并不会造成程序卡顿的现象。

不过并发执行描绘出的对象图谱在运行过程中可能会被更改,比如已经标记为垃圾的对象又重新成为存活对象,或者已经标记为存活对象的又变成了垃圾对象。

因此就需要下一阶段的工作。

重新标记:这一阶段需要进行简单的扫描,来修正那些在并发标记过程中,由于后续程序运行导致标记发生变化的对象。这一阶段需要的STW的时间远远小于并发标记的时间,所以对程序整体的运行影响不大。

并发清理:这一步是将上面几步发现的垃圾对象进行清理。同样是与工作线程并发执行的。

通过将耗时的垃圾标记算法与工作线程并发执行,达到了降低整体STW的时间,就是CMS的思想核心。

不过CMS也有其问题,这也是为什么CMS从来也没有成为JVM默认垃圾回收器的原因。

CMS的问题

首先会产生浮动垃圾的问题,在并发清理的阶段,在一边清理的过程中,又有了新垃圾的产生,不过这些浮动垃圾在下一次CMS运行周期会被处理掉。

另一个更大的问题就是,由于CMS内部是使用了mark-sweep这种垃圾清除算法,所以mark-sweep带来的内存碎片化问题同样困扰着CMS。

所以当CMS造成的内存碎片化问题严重到没法分配新对象的时候,就会转换成Serial Old垃圾回收器对整个内存进行回收操作。

这就造成了一个问题,CMS在正常的情况下运行速度会比较理想,但一点出现问题,就会造成整个系统非常长时间的卡顿。

总结

本文主要讲了一些常用的垃圾回收器及其常用组合的特性,包括单线程的Serial + Serial Old,多线程的Parallel Scavenge + Parallel Old,还有可以让垃圾回收线程与工作线程并发执行的ParNew + CMS。

而关于最新的G1垃圾回收器,我们会在之后的文章中进行讲解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值