大佬教程收集整理的这篇文章主要介绍了终于把CMS垃圾收集器搞懂了~,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
CMS GC的官方名称为“Mostly Concurrenct Mark and Sweep Garbage Collector”(最大-并发-标记-清除-垃圾收集器)。作用范围: 老年代算法: 并发标记清除算法。启用参数:-XX:+UseConMarkSweepGC
默认回收线程数:(处理器核心数量 + 3)/4Java9之后使用CMS垃圾收集器后,默认年轻代就为ParNew收集器,并且不可更改,同时JDK9之后被标记为不推荐使用,JDK14就被删除了。
并发与并行有什么区别? 并行(Parallel):并行描述的多条垃圾收集器线程之间的关系,说明同一时间有多条这样的线程在协同工作,通常默认此时用户线程为等待状态。 并发(Concurrent):并发描述的垃圾收集线程与用户线程之间的关系,说明同一时间垃圾收集线程与用户线程都在工作,由于用户线程并未冻结,因此还能继续相应服务请求,但由于垃圾收集器线程占用了一定的系统资源,此时应用程序处理的吞吐量将受到一定影响。
设计目标/优点:避免在老年代垃圾收集时出现长时间的卡顿,主要通过两种手段来达成此目标:
适用场景:
如果服务器是多核CPU,并且主要调优目标是降低GC停顿导致的系统延迟,那么使用CMS是个很明智的选择。通过减少每一次GC停顿的时间,很多时候会直接改善用户体验。因为多数情况下有部分CPU资源被垃圾回收器线程消耗,所以在CPU资源受限的情况下,CMS GC会比并行GC的吞吐量差一些(对于绝大部分系统,这个吞吐和延迟的差别应该都不明显) 在实际情况中,进行老年代的并发回收时,可能会伴随多次年轻代的minor GC。在这种情况下full GC的日志中就会掺杂着多次minor GC事件
这个阶段会STW。工作模式: JDK7之前单线程,JDK8之后多线程目标: 标记所有的根对象,包括根对象直接引用的对象,以及被年轻代中所有存活的对象所引用的老年代对象(只是标记一下GC Roots能直接关联到的对象,速度很快)
此阶段,CMS GC遍历所有的对象,标记存活的对象,从前一阶段“初始标记”找到的根元素开始算起。“并发标记”阶段就是与应用程序同时运行,不用暂停的阶段。此阶段由于与用户线程并发执行,对象的状态可能会发生变化,如下:
JVM会通过Card(卡片)
的方式将发生改变的老年代区域标记为“脏”区,这就是所谓的卡片标记(Card Marking)
在上边的图中,“当前处理的对象”的一个引用就被应用线程给断开了,即这个部分的对象关系发生了变化
也是用于标记老年代存活的对象,此阶段仍然是与应用线程并发执行的,不需要停止应用线程。目的: 让最终/重新标记的STW时间尽可能短标记目标:
关闭参数:-XX:-CMSPrecleaningEnabled
,默认开启
此阶段也不停止应用程序,本阶段尝试在STW的最终标记阶段之前尽可能多做一些工作。本阶段的具体时间取决于多种因素,因为它循环做同样的事情,直到满足某个退出条件(如迭代次数、有用工作量、消耗的系统时间等等) 目标: 与并发预处理一样,为了使最终/重新标记的STW时间尽可能短 价值: 在进入最终标记前尽量等到一个Minor GC,尽量缩短最终标记阶段的停顿时间触发条件: 在预清理步骤后,如果满足下面这个个条件,就会开启可中断的预清理,直接进入重新标记阶段
-XX:CMSScheduleRemarkEdenSizeThreshold
”,这个参数的默认值是2M;取消条件:
CMSMaxAbortablePrecleanLoops
循环次数,并且执行的次数大于或者等于这个值的时候。默认为0CMSMaxAbortablePrecleanTime
,执行可中断预清理的时间超过了这个值,这个参数的默认值是5000毫秒-XX:CMSScheduleRemarkEdenPenetration
,这个参数的默认值是50%。问题: 可能在可取消的并发预处理过程中一直没等到Minor GC,这个时候进行最终标记的话,可能会发生连续停顿,假设新生代在最终标记的时候发生了Minor GC(STW),最终标记又是STW的,因此可能会发生连续停顿,CMS提供了参数CMSScavengeBeforeRemark
使最终/重新标记前强制进行一次Minor GC(其实这样也会导致连续停顿,Minor和Remark)。
最终标记是此阶段GC事件中的第二次(也是最后一次)STW停顿。目标: 重新扫描堆中的对象,因为之前的预清理阶段是并发执行的,有可能GC线程跟不上应用程序的修改速度。扫描范围: 新生代对象+GC Roots+被标记为“脏”区的对象。如果预清理阶段没有做好,这一步扫描新生代的时候就会花很多时间。
此阶段与应用程序并发执行,不需要STW停顿。JVM在此阶段删除不再使用的对象,并回收他们占用的内存空间。因为阶段5已经把所有还在使用的对象进行了标记,因此此阶段可以与应用线程并发的执行。
此阶段与应用程序并发执行,重置CMS算法相关的内部数据,为下一次GC循环做准备。 总之,CMS垃圾收集器在减少停顿时间上做了很多复杂的而有用的工作,用于垃圾回收的并行线程执行的同时,并不需要暂停应用线程。
CMS会根据历史记录,预测老年代还有多长时间会满及进行一次回收所需的时间,可以使用参数_-XX:+UseCMSInitiatingOccupancyOnly_
来关闭,开启这个参数后,配置的回收阈值-XX:CMSInitiatingOccupancyFraction=N
会长期生效,否则只会第一次生效
-XX:+UseCMSCompactAtFullCollection(默认开启,JDK9废弃)
,在进行Full GC之前进行一次内存整理(无法并发,Shenandoah和ZGC可以),-XX:CMSFullGCBeforeCompaction=n(默认为0,表示每次进入Full GC时都进行碎片整理)
,参数作用是当CMS收集器执行过n次不整理内存碎片后,下一次进入Full GC前先进行碎片整理-XX:CMSInitiatingOccupancyFraction_=数值_
+ -XX:+UseCMSInitiatingOccupancyOnly
来设置)
-XX:CMSInitiatingOccupancyFraction
过大时,就可能会出现垃圾收集过程中无法分配对象的问题,导致“并发失败”(Concurrent Mode Failure),此时会临时启用Serial Old收集器来重新进行老年代收集,这会导致停顿时间更长
-XX:CMSInitiatingOccupancyFraction=数值
+ -XX:+UseCMSInitiatingOccupancyOnly
,-XX:CMSInitiatingOccupancyFraction
调小,但也不能太小,太小了会导致过多无效的收集,浪费资源
-XX:+CMSPermGenSweepingEnabled
开启对方法区的收集,开启后会有专门的一组线程对永久代进行垃圾回收,同时还需要开启另一个参数-XX:+CMSClassUnloadingEnabled
,使得在垃圾收集时可以卸载不用的类。
如果系统追求低延迟,那么可以选择CMS垃圾收集器,只是STW的时间缩短了,但是整个GC的时间相对更长了;如果系统追求高吞吐,那么可以选择并行Parallel GC,虽然STW的时间长,但是可以保证非GC时间,整个系统的资源全部被应用线程占用。
以上是大佬教程为你收集整理的终于把CMS垃圾收集器搞懂了~全部内容,希望文章能够帮你解决终于把CMS垃圾收集器搞懂了~所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。