21-看懂CMS收集器工作机制

简介: 年轻代垃圾回收器机制我们都很清楚了,接下来我们介绍最核心的老年代垃圾回收环节。

CMS收集器

我们平时在写代码的时候,相信大部分同学几乎都没有考虑过垃圾回收啥的吧?就是不停的疯狂写代码,然后直接上线部署,也不考虑我们写的代码对内存是否有影响,对垃圾回收是否有影响。然后当系统运行起来一段时间后,就发现各种卡顿,频繁触发Full GC。

类似的情况有很多,我们不能过于理想化的期待永远没有Full GC,还是要针对老年代的垃圾回收器的工作原理做到心理有数,从而更好的做调优工作。

一般老年代我们选择的垃圾回收器就是CMS(Concurrent Mark Sweep) 收集器 ,这是一种以获取最短回收停顿时间为目标的收集器。 我们大部分互联网网站或者基于浏览器的B/S系统的服务端 ,这类应用通常都会较为关注服务的响应速度, 希望系统停顿时间尽可能短, 以给用户带来良好的交互体验。 CMS收集器就非常符合这类应用的需求。

从名字(包含“Mark Sweep”) 上就可以看出CMS收集器是基于标记-清除算法实现的, 它的运作
过程相对于前面几种收集器来说要更复杂一些, 整个过程分为四个步骤, 包括:

1) 初始标记(CMS initial mark)

2) 并发标记(CMS concurrent mark)

3) 重新标记(CMS remark)

4) 并发清除(CMS concurrent sweep)

接下来我们来一步一步分析整个CMS的运行逻辑和流程,尽量把它的核心思路掌握。

1) 初始标记(CMS initial mark)

首先根据之前讲过的“可达性分析算法“来判断有哪些对象是被GC Roots给引用的,如果是的话就是存活对象,否则就是垃圾对象。然后将垃圾对象都标记出来,如下图:


注意:初始标记的过程会让系统停止工作,进入“Stop The World”状态,不过这个过程很快,仅仅标记GCRoots直接引用的那些对象。(回顾下GC Roots对象有:类的静态变量,方法的局部变量,但是类的实例变量不是GCRoots)

假设我现在系统中有这样一段代码:

public class Test{
   
   
    private static Company company = new Company();
}
public class Company{
   
   
    private Employee employee = new Employee();
}

那么在内存中对应的初始标记阶段只会标记出来GC Roots直接引用的对象,也就是Company()对象,而employee对象仅仅是类的实例变量,不会被进行标记。内存图如下:


注意:Employee对象仅仅是类的实例变量引用的对象,不是GCRoot直接引用的对象,因此初始标记并不会进行标记。

2)并发标记(CMS concurrent mark)

并发标记阶段恢复系统正常运行,可以随意创建对象,同时并发标记线程也开始工作,这里由于一边进行并发标记,一边进行对象的创建,必然会持续增加新的对象产生,同时也有可能一些对象失去引用变成垃圾对象。

那么并发标记主要是标记哪些对象呢?比如上图中的Employee对象,垃圾回收线程会判断该对象被谁引用,这里是被company对象引用,再次判断company对象被谁引用,由于初始标记的时候已经知道是被GCRoots直接引用,从而判断到Employee对象是间接被GCRoots对象引用,从而标记为存活对象。

总之,针对所有老年代中存在的对象以及不断新增的对象都会进行标记,而我们的系统线程也在一直工作不断产生对象,所以该阶段也是最耗时的。虽然是耗时的,但是垃圾回收与系统是并行进行的,所以并不会对系统的运行造成影响。

3)重新标记(CMS remark)

由于我们的第二个阶段是并发标记,那么肯定会造成有部分对象已经失去引用变成垃圾对象没有来得及更正,以及新创建的对象还未来得及标记,如下图:


因此第三阶段:重新标记 会暂停我们的系统线程,开始重新整理,如下图:

不过该阶段会很快,主要是针对第二阶段中被系统程序运行变动过的少数对象进行标记,所以速度很快。

接着重新恢复系统线程工作,开始进入第四阶段:并发清理。

4)并发清除(CMS concurrent sweep)

最后是并发清除阶段, 清理删除掉标记阶段判断的已经死亡的对象, 由于不需要移动存活对象, 所以这个阶段也是可以与用户线程同时并发的。

5)小结

通过以上CMS工作的整个过程,我们总结下:

  • 最耗时的阶段是:并发标记与并发清除--->不过该阶段是与用户线程并发执行并不影响系统
  • 初始标记和重新标记阶段:需要Stop the World,暂停系统工作---->但是该两个阶段速度很快几乎影响不大

通过一张完整的流程图来表示我们CMS的工作逻辑:

目录
相关文章
|
6月前
|
算法 Java
jvm性能调优 - 15JVM的老年代垃圾回收器CMS的缺点
jvm性能调优 - 15JVM的老年代垃圾回收器CMS的缺点
122 0
|
Java
G1垃圾回收器的工作流程
G1垃圾回收器的工作流程
1842 0
|
5月前
|
算法 Java 云计算
JVM垃圾回收的历史演进:从GC算法到垃圾回收器选择
JVM垃圾回收的历史演进:从GC算法到垃圾回收器选择
|
6月前
|
存储 算法 Java
深入浅出JVM(十七)之并发垃圾收集器CMS
深入浅出JVM(十七)之并发垃圾收集器CMS
|
6月前
|
监控 Java Linux
JVM工作原理与实战(三十七):Shenandoah GC和ZGC
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了垃圾回收器的技术演进、Shenandoah GC、ZGC等内容。
137 0
|
存储 算法 Java
JVM学习日志(十三) G1垃圾回收流程 及 垃圾回收器总结
G1垃圾回收流程 及 垃圾回收器 总结 简述
205 0
JVM学习日志(十三) G1垃圾回收流程 及 垃圾回收器总结
|
缓存 算法 Java
JVM CMS GC算法解析
JVM CMS GC算法解析
80 0
|
前端开发 Java
Java虚拟机 CMS GC 调优解析
随着 JDK 版本的不断升级,其 GC 策略也随之不停革新,从早期的 1.4 到如今的 11(本文仅讨论在线上环境落地规模较大的版本),其对应的 GC 策略也随之由 Serial、Parallel、CMS 演进至当前的 G1 甚至即将落地的 ZGC 。每一次的调整无不是基于环境的适配性以及业务场景特性,无论如何,只要能够基于特定的操作系统内核、物理内存、JDK版本以及业务特性,达到收益最大化,采用何种实现策略都不为过。当然,还是建议大家以官方的推荐为准,基于自己的业务场景进行不断优化调整,这样才能保证万无一失,使得我们的业务能够健康发展。
165 0
|
存储 缓存 算法
详述JVM的GC及垃圾回收策略
详述JVM的GC及垃圾回收策略
926 2
详述JVM的GC及垃圾回收策略
|
算法 NoSQL Java
深入理解JVM - CMS收集器
上一篇文章我们讲解分代的基础理论,同时讲解了新生代和老年代各自的算法复制算法和标记整理算法,之后我们总结了新生代进入老年代的条件,在最后我们介绍的引用类型,同时进行了练习的提问和相关的解答。
93 0