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的工作逻辑:

目录
相关文章
|
4月前
|
算法 Java
jvm性能调优 - 15JVM的老年代垃圾回收器CMS的缺点
jvm性能调优 - 15JVM的老年代垃圾回收器CMS的缺点
66 0
|
4月前
|
消息中间件 算法 Java
jvm性能调优 - 14JVM的老年代垃圾回收器CMS原理
jvm性能调优 - 14JVM的老年代垃圾回收器CMS原理
49 0
|
Java
G1垃圾回收器的工作流程
G1垃圾回收器的工作流程
1510 0
|
2月前
|
存储 缓存 算法
五、垃圾收集基础
五、垃圾收集基础
31 3
|
6月前
|
缓存 算法 Java
JVM CMS GC算法解析
JVM CMS GC算法解析
43 0
|
10月前
|
前端开发 Java
Java虚拟机 CMS GC 调优解析
随着 JDK 版本的不断升级,其 GC 策略也随之不停革新,从早期的 1.4 到如今的 11(本文仅讨论在线上环境落地规模较大的版本),其对应的 GC 策略也随之由 Serial、Parallel、CMS 演进至当前的 G1 甚至即将落地的 ZGC 。每一次的调整无不是基于环境的适配性以及业务场景特性,无论如何,只要能够基于特定的操作系统内核、物理内存、JDK版本以及业务特性,达到收益最大化,采用何种实现策略都不为过。当然,还是建议大家以官方的推荐为准,基于自己的业务场景进行不断优化调整,这样才能保证万无一失,使得我们的业务能够健康发展。
97 0
|
12月前
|
算法 Java
JVM的GC机制和常见GC算法
@[toc] 1. 堆内存的分代 2. GC分类 3. 什么是GC 3.1 需要GC的内存区域 3.2 GC回收的对象 3.3 判断对象存活的两种算法 3.3.1 引用计数 3.3.2 可达性分析 3.4 什么时候触发GC 4. 常见的GC算法 4.1 标记-清除算法 4.2 复制算法 4.3 标记-压缩算法
99 0
|
存储 负载均衡 算法
6000字吃透JVM垃圾回收器:并发标记清除回收,并行的新生代回收
CMS新生代回收相比串行新生代回收最大的优化是将串行算法升级为并行算法。 并行回收在CMS中被称为ParNew。从串行到并行需要考虑的问题是:如何让多个线程并行地执行任务?如果多个并行线程任务负载不均衡该如何处理?如何判断多个线程并行执行结束?
|
存储 缓存 算法
详述JVM的GC及垃圾回收策略
详述JVM的GC及垃圾回收策略
875 2
详述JVM的GC及垃圾回收策略
|
算法 安全 Java
【JVM性能优化】CMS回收器的Full-GC流程分析以及问题探究
【JVM性能优化】CMS回收器的Full-GC流程分析以及问题探究
552 0
【JVM性能优化】CMS回收器的Full-GC流程分析以及问题探究