深入剖析JVM垃圾回收-下

6 篇文章 1 订阅

CMS垃圾收集器

GC类型

  • Minor GC ,发生在年轻代GC
  • Major GC,发生老年代GC
  • Full GC,全堆垃圾回收,如MetaSpace区引起的年轻代和老年代的回收

CMS简述

		1.CMS全称是Mostly Concurrent Mark and Sweep Garbage Collector
		2.年轻代使用copy(Eden from  to区域)算法,而老年代使用Mark-sweep方法。
		3.可以看到,老年代比起Mark-Sweep多了并发操作记录。
		4.CMS致力于避免老年代GC时出现长时间卡顿(但它并不是一个老年代回收器)。如果不希望长时间卡顿,同时CPUS资源标记丰富,使用CMS较合适。
		5.CMS使用的是Sweep而不是Compact(内存整理算法),主要问题是内存碎片化。随着jvm长时间运行,碎片化越来越重,只有通过Full GC才能完成
		6.CMS 效率提高得利于把最耗时的工作(Mark),做成了和应用线程并行。

CMS 回收过程

标记过程

初始标记(initial Mark)

  • STW,标记GCRoots 直接关联的对象
    初始标记阶段,只标记直接关联GC Root对象,不用向下追溯。因为最耗时时间就在追溯阶段(tracing)阶段,这样极大缩短初始标记时间。
    该过程是STW,用户线程无法进行工作。但只标记第一层,所以速度很快。
    该过程除了标记相关的GC Roots之外,还要标记年轻代中对象的引用,这也是CMS老年代回收,依然要扫描年轻代的原因。
    
  • 图示
    初始标记

并发标记(concurrent Mark)

  • 追踪与GC Roots间接关联的所有可达对象
    在初始标记基础上,进行并发标记。
    该步骤主要是tracing过程,用于标记所有可达对象
    
  • 标记进程与用户线程并行
    持续时间较长,但可以和用户线程并行。
    该过程会会产生诸多变化:
    1.有些对象,从新生代晋升到老年代(通过age=阈值 提升到老年代)
    2.有些对象,直接分配到了老年代(分配担保)
    3.老年代或者新生代对象引用发生变化(通过卡片记录老年代对新生代对象的引用,引用则是脏页)
    
  • 图示
    并发标记回收对象

清理过程

并发预清理

  • 非STW预清理
    并发地预清理,也不需STW,目的让每次重新进行的初始标记阶段的STW时间尽可能缩短。
    该阶段,老年代中被标记dirty的卡页,就会被重新标记,然后就会被清除dirty状态
    该阶段也可以是并发的的,在执行过程中引用关系依然会发生一些变化。可以假定这个清理动作是第一次清理。因此,重新标记阶段,有可能仍然有处于dirty状态。
    

并发可取消的预清理

  • 可选、可终止的并发预清理
    重新标记需要STW,所以会有很多次的预清理动作。
    并发可取消的预清理,顾名思义,在满足某些条件时,可以终止,比如迭代次数、有用工作量、消耗的系统实际等
    该阶段是可选的,是”并发预清理”的阶段的一种优化
    该阶段目的是,避免扫描年轻代的大量对象(标记动作需要扫描年轻代);当满足最终标记的条件时,自动退出。
    标记动作是需要扫描年轻代。若年轻代对象过多,肯定严重影响标记效率,如果在此之前能够进行一次minor GC,情况会有所改善。CMS提供了参数CMSScavengeBeforeRemark,可以在进入重新标记之前强制进行一次Minor GC.
    GC停顿不分年轻代和年老代。设置CMSScavengeBeforeRemark,可能会在一个比较长的Minor GC之后,紧接一个CMS的Remark,他们都是STW的。
    类似CMSScavengeBeforeRemark非常多的配置参数,属于jdk提供通用场景最佳配置,一般不会修改
    

最终标记(Final Remark)

  • 二次STW对老年代存活对象标记
    1.通常CMS会尝试在年轻代尽可能空的情况下运行Final Remark,以免连续多次发生STW事件
    2.该阶段是CMS GC的第二次STW阶段,目的完成老年代所有存活对象标记。之前多轮preclean阶段,一直在和应用线程相互追赶,有可能跟不上引用的变化速度。本轮标记动作需要STW来处理这个问题
    3.如果预处理做的做的不好,会显著增加Final Remark的STW时间
    4.从该处看到CMS将垃圾回收分了多个部分,而影响最大的不是STW本身,而是它之前预处理动作(效果不好,会显著增加二阶段Final Remark STW时间)
    

并发清除(Concurrent Sweep)

  • GC线程和应用线程并发清除垃圾
    1.该阶段,用户线程被重新激活,目标是删除不可达对象,回收器空间
    2.由于CMS并发清理阶段和用户线程并发执行,伴随程序运行自然会有新的垃圾,CMS无法在当次GC中处理掉,只好待下次GC时进行清理,这部分垃圾成为”浮动垃圾”
    
  • GC 与应用线程一起运行,GC垃圾时产生浮动垃圾
    与用户线程一起GC时产生浮动垃圾

并发重置(Concurrent Rest)

  • 此阶段和应用线程并发执行,重置CMS算法相关内部数据,为下次GC循环做准备

内存碎片

  • CMS 老年代回收问题
    	1.CMS执行过程中,用户线程也在运行。这就需要保证充足内存空间提供给来用户使用。
    	2.如果老年代空间接近占满,再启动该回收过程,用户线程会产生”concurrent Mode Failure”错误,这是临时启用Serial old收集器来重新对老年代垃圾收集,那么停顿(STW)时间会更惨。
    	3.为避免老年代空间占满,老年代空间需要预留30%,大概能用的只有70%。使用-XX:CMSInitiatingOccupancyFraction用来配置这个比例(首先开启参数UseCMSInitiatingOccupancyOnly).也就是说,当老年代使用达到70%,就会触发GC,不会造成程序报错,影响运行。如果老年代增长不太快,可以调高该参数,降低内存回收次数。
    	4.老年代内存比率不太好设置,一般堆大小小于2GB时,都不会考虑CMS垃圾收集器。
    	5.CMS对老年代回收时,并未进行内存整理。这会造成内存运行过久后,产生大量碎片。如申请稍大对象空间,就会引起分配失败
    	6.CMS提供两个参数解决该内存碎片问题
    	(1)UseCMSCompactAtFullCollect(默认开启),FullGC时,进行内存碎片整理。内存整理无法并发,所以停顿会较长
    	(2)CMSFullGCBeforeCompaction,每隔多少次不压缩的Full GC后,执行一次带压缩的Full GC。默认为0,表示每次进入Full GC都有进行碎片整理。
    	7.预留内存空间加上内存碎片,使用CMS垃圾回收器的老年代,留给使用的空间不是太多,也是CMS的一个弱点。
    	````
    
  • 老年代可用空间老年代可用空间

小结

  • CMS 垃圾收集四阶段

    1.初始标记(initial mark)
    2.并发标记(GC,应用并发执行)
    3.重新标记(Final Remark,STW)
    4.并发清理(cocurrent sweep)
    
  • 引起STW的阶段

    1.初始标记,GC Roots直接对象标记,部分停顿时间较短。
    2.Minor GC(并发可取消预清理,可选),在预处理阶段对年轻代的回收,STW由年轻代决定。
    3.重新标记,由于preclean阶段介入,该部分停顿也较短
    4.Serial old收集老年代的停顿,发生在预留空间不足的情况下,时间持续较长
    5.Full GC,永久代空间耗尽时的操作,由于会有整理阶段,时间加长。
    
  • 发生GC是,确定发生在哪个阶段,对症下药,GC log通常有非常详细的通常

  • CMS trade-off

    优点:低延迟,对大堆来说。大部分垃圾回收过程并发执行
    缺点:
    1.内存碎片。需要Full GC 进行内存整理,会造成较长卡顿
    2.需要为用户正常使用时,要求老年代预留空间,防止内存占满,影响用户体验,分配收集阶段产生的”浮动垃圾”
    3.使用更多CPU资源,应用运行的同时进行堆扫描。
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值