zoukankan      html  css  js  c++  java
  • JAVA CMS垃圾回收器回收机制

    CMS concurrent marks sweep 并行标记清除垃圾回收机制。此篇文章是根据众多网上资料总结的关于CMS垃圾回收器的相关知识点。便于个人总结和回忆。

    垃圾回收器类型

    1、串行回收,Serial回收器,单线程回收,全程stw;
    2、并行回收,名称以Parallel开头的回收器,多线程回收,全程stw;
    3、并发回收,cms与G1,多线程分阶段回收,只有某阶段会stw;

    CMS垃圾回收器特点

    1、cms只会回收老年代和永久代(1.8开始为元数据区,需要设置CMSClassUnloadingEnabled),不会收集年轻代;
    2、cms是一种预处理垃圾回收器,它不能等到old内存用尽时回收,需要在内存用尽前,完成回收操作,否则会导致并发回收失败;所以cms垃圾回收器开始执行回收操作,有一个触发阈值,默认是老年代或永久带达到92%;

    CMS垃圾回收的七大步骤

    找到两张图来帮助理解这几个步骤:
    图1:
    在这里插入图片描述
    图2:
    在这里插入图片描述

    以下过程参考博客:https://www.jianshu.com/p/2a1b2f17d3e4
    1、初始标记:会发生stw
    单线程执行
    a/标记GC ROOT可达的老年代对象
    b/遍历新生代对象,标记可达的老年代对象;
    该阶段执行完以后,如图1,灰色标记对象。
    2、并发标记
    和应用程序并发执行
    a/ 1阶段完成后,需要继续递归遍历,标记这些对象的可达对象。
    因为该阶段并发执行的,在运行期间可能发生新生代的对象晋升到老年代、或者是直接在老年代分配对象、或者更新老年代对象的引用关
    系等等,对于这些对象,都是需要进行重新标记的,否则有些对象就会被遗漏,发生漏标的情况。如图2
    为了提高重新标记的效率,该阶段会把上述对象所在的Card标识为Dirty,后续只需扫描这些Dirty Card的对象,避免扫描整个老年代。

    3、预清理
    a/处理新生代新发现的引用,比如在并发阶段,在Eden区中分配了一个A对象,A对象引用了一个老年代对象B(这个B之前没有被标
    记),在这个阶段就会标记对象B为活跃对象。
    b/在并发标记阶段,如果老年代中有对象内部引用发生变化,会把所在的Card标记为Dirty(其实这里并非使用CardTable,而是一个
    类似的数据结构,叫ModUnionTalble),通过扫描这些Table,重新标记那些在并发标记阶段引用被更新的对象(晋升到老年代的对象/
    原本就在老年代的对象)

    4、可被终止的预清理

    a/处理 From 和 To 区的对象,标记可达的老年代对象
    b/和上一个阶段一样,扫描处理Dirty Card中的对象
    
    该阶段发生的前提是,新生代Eden区的内存使用量大于参数CMSScheduleRemarkEdenSizeThreshold 默认是2M,如果新生代的对象
    太少,就没有必要执行该阶段,直接执行重新标记阶段。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    为什么需要这个阶段,存在的价值是什么?

    	因为CMS GC的终极目标是降低垃圾回收时的暂停时间,所以在该阶段要尽最大的努力去处理那些在并发阶段被应用线程更新的老年代对象,这样在暂停的
    	重新标记阶段就可以少处理一些,暂停时间也会相应的降低。
    
    • 1
    • 2

    当然了,这个逻辑不会一直循环下去,打断这个循环的条件有三个:

    	1/可以设置最多循环的次数 CMSMaxAbortablePrecleanLoops,默认是0,意思没有循环次数的限制。
    	2/如果执行这个逻辑的时间达到了阈值CMSMaxAbortablePrecleanTime,默认是5s,会退出循环。
    	3/如果新生代Eden区的内存使用率达到了阈值CMSScheduleRemarkEdenPenetration,默认50%,会退出循环。(这个条件能够成立的前提是,
    	在进行Precleaning时,Eden区的使用率小于十分之一)
    
    • 1
    • 2
    • 3
    • 4

    如果在循环退出之前,发生了一次YGC,对于后面的Remark阶段来说,大大减轻了扫描年轻代的负担,但是发生YGC并非人为控制,所以只能祈祷这5s内可以来一次YGC。

    5、重新标记,会发生stw

    在之前的过程中还会存在一些未标记的对象主要包括:
    
    	1、老年代的新对象被GC Roots引用
    	2、老年代的未标记对象被新生代对象引用
    	3、老年代已标记的对象增加新引用指向老年代其它对象
    	4、新生代对象指向老年代引用被删除
    也许还有其它情况..
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上述对象中可能有一些已经在Precleaning阶段和AbortablePreclean阶段被处理过,但总存在没来得及处理的,所以还有进行如下的处理:

    1、遍历新生代对象,重新标记
    2、根据GC Roots,重新标记
    3、遍历老年代的Dirty Card,重新标记,这里的Dirty Card大部分已经在clean阶段处理过
    
    这个阶段暂停所有用户线程,重新扫描堆中的对象,
    进行可达性分析,标记活着的对象。
    有了前面的基础,这个阶段的工作量被大大减轻,停顿时间因此也会减少。
    
    注意这个阶段是多线程的。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    6、并发清除

    7、并发重置
    为下个阶段的垃圾回收做准备。

    其他关于jvm的博客:
    GC和FULLGC发生时间:https://blog.csdn.net/jialiuyang521/article/details/87982459

    STW:
    https://www.jianshu.com/p/d686e108d15f

    转载:https://blog.csdn.net/wangyy130/article/details/88758055

    参考:https://www.cnblogs.com/dim2046/p/12059622.html

  • 相关阅读:
    JAVA学习日报 11/26
    JAVA学习日报 11/25
    大二寒假作业之JavaWeb
    大二寒假作业之JavaWeb
    大二寒假作业之JavaWeb
    大二寒假作业之《构建之法》读后感2
    大二寒假作业之Android
    大二寒假作业之《构建之法》读后感1
    大二寒假作业之android
    大二寒假作之Android
  • 原文地址:https://www.cnblogs.com/wangyingshuo/p/14428808.html
Copyright © 2011-2022 走看看