不可错过的CMS学习笔记

引子

带着问题去学习一个东西,才会有目标感,我先把一直以来自己对CMS的一些疑惑罗列了下,希望这篇学习笔记能解决掉这些疑惑,希望也能对你有所帮助。

  1. CMS出现的初衷、背景和目的?

  2. CMS的适用场景?

  3. CMS的trade-off是什么?优势、劣势和代价

  4. CMS会回收哪个区域的对象?

  5. CMS的GC Roots包括那些对象?

  6. CMS的过程?

  7. CMS和Full gc是不是一回事?

  8. CMS何时触发?

  9. CMS的日志如何分析?

  10. CMS的调优如何做?

  11. CMS扫描那些对象?

  12. CMS和CMS collector的区别?

  13. CMS的推荐参数设置?

  14. 为什么ParNew可以和CMS配合使用,而Parallel Scanvenge不可以?

一、基础知识

  1. CMS收集器:Mostly-Concurrent收集器,也称并发标记清除收集器(Concurrent Mark-Sweep GC,CMS收集器),它管理新生代的方式与Parallel收集器和Serial收集器相同,而在老年代则是尽可能得并发执行,每个垃圾收集器周期只有2次短停顿。

  2. 我之前对CMS的理解,以为它是针对老年代的收集器。今天查阅了《Java性能优化权威指南》和《Java性能权威指南》两本书,确认之前的理解是错误的。

  3. CMS的初衷和目的:为了消除Throught收集器和Serial收集器在Full GC周期中的长时间停顿。

  4. CMS的适用场景:如果你的应用需要更快的响应,不希望有长时间的停顿,同时你的CPU资源也比较丰富,就适合适用CMS收集器。

二、CMS的过程

CMS的正常过程

这里我们首先看下CMS并发收集周期正常完成的几个状态。

1.(STW)初始标记:这个阶段是标记从GcRoots直接可达的老年代对象、新生代引用的老年代对象,就是下图中灰色的点。这个过程是单线程的。

6e89fc0c6c0d1c4e222cfed13c9428b89eb778c3


2. 并发标记:由上一个阶段标记过的对象,开始tracing过程,标记所有可达的对象,这个阶段垃圾回收线程和应用线程同时运行,如上图中的黄色的点。在并发标记过程中,应用线程还在跑,因此会导致有些对象会从新生代晋升到老年代、有些老年代的对象引用会被改变、有些对象会直接分配到老年代,这些受到影响的老年代对象所在的card会被标记为dirty,用于重新标记阶段扫描。这个阶段过程中,老年代对象的card被标记为dirty的可能原因,就是下图中绿色的线:

0da5d6974f96ed4447edf697665fc19bf83b49a0


3. 预清理:预清理,也是用于标记老年代存活的对象,目的是为了让重新标记阶段的STW尽可能短。这个阶段的目标是在并发标记阶段被应用线程影响到的老年代对象,包括:(1)老年代中card为dirty的对象;(2)幸存区(from和to)中引用的老年代对象。因此,这个阶段也需要扫描新生代+老年代。【PS:会不会扫描Eden区的对象,我看源代码猜测是没有,还需要继续求证】

a5ec2ccbcbe12057e166dfa57dd8aef5da3f1ef1


4. 可中断的预清理:这个阶段的目标跟“预清理”阶段相同,也是为了减轻重新标记阶段的工作量。可中断预清理的价值:在进入重新标记阶段之前尽量等到一个Minor GC,尽量缩短重新标记阶段的停顿时间。另外可中断预清理会在Eden达到50%的时候开始,这时候离下一次minor gc还有半程的时间,这个还有另一个意义,即避免短时间内连着的两个停顿,如下图资料所示:

c55ad004b2ada7597e8dce57cc9f6a32a63d31c0
在预清理步骤后,如果满足下面两个条件,就不会开启可中断的预清理,直接进入重新标记阶段:

如果不满足上面两个条件,则进入可中断的预清理,可中断预清理可能会执行多次,那么退出这个阶段的出口有两个(源码参见下图):


  • 设置了CMSMaxAbortablePrecleanLoops,并且执行的次数超过了这个值,这个参数的默认值是0;

  • CMSMaxAbortablePrecleanTime,执行可中断预清理的时间超过了这个值,这个参数的默认值是5000毫秒。

    3e54929185056cbfeddb809cfed58f195b66862e
    如果是因为这个原因退出,gc日志打印如下:
    4aac0dc5a8b85e9cdb2931c96eb12d22a6b707c1

    有可能可中断预清理过程中一直没等到Minor gc,这时候进入重新标记阶段的话,新生代还有很多活着的对象,就回导致STW变长,因此CMS还提供了CMSScavengeBeforeRemark参数,可以在进入重新标记之前强制进行依次Minor gc。

  • Eden的使用空间大于“CMSScheduleRemarkEdenSizeThreshold”,这个参数的默认值是2M;

  • Eden的使用率大于等于“CMSScheduleRemarkEdenPenetration”,这个参数的默认值是50%。


5.(STW)重新标记:重新扫描堆中的对象,进行可达性分析,标记活着的对象。这个阶段扫描的目标是:新生代的对象 + Gc Roots + 前面被标记为dirty的card对应的老年代对象。如果预清理的工作没做好,这一步扫描新生代的时候就会花很多时间,导致这个阶段的停顿时间过长。这个过程是多线程的。

6. 并发清除:用户线程被重新激活,同时将那些未被标记为存活的对象标记为不可达;

7. 并发重置:CMS内部重置回收器状态,准备进入下一个并发回收周期。

CMS的异常情况

上面描述的是CMS的并发周期正常完成的情况,但是还有几种CMS并发周期失败的情况:

  1. 并发模式失败(Concurrent mode failure):CMS的目标就是在回收老年代对象的时候不要停止全部应用线程,在并发周期执行期间,用户的线程依然在运行,如果这时候如果应用线程向老年代请求分配的空间超过预留的空间(担保失败),就回触发concurrent mode failure,然后CMS的并发周期就会被一次Full GC代替——停止全部应用进行垃圾收集,并进行空间压缩。如果我们设置了UseCMSInitiatingOccupancyOnly和CMSInitiatingOccupancyFraction参数,其中CMSInitiatingOccupancyFraction的值是70,那预留空间就是老年代的30%。

  2. 晋升失败:新生代做minor gc的时候,需要CMS的担保机制确认老年代是否有足够的空间容纳要晋升的对象,担保机制发现不够,则报concurrent mode failure,如果担保机制判断是够的,但是实际上由于碎片问题导致无法分配,就会报晋升失败。

  3. 永久代空间(或Java8的元空间)耗尽,默认情况下,CMS不会对永久代进行收集,一旦永久代空间耗尽,就回触发Full GC。

三、CMS的调优

1. 针对停顿时间过长的调优 首先需要判断是哪个阶段的停顿导致的,然后再针对具体的原因进行调优。使用CMS收集器的JVM可能引发停顿的情况有:(1)Minor gc的停顿;(2)并发周期里初始标记的停顿;(3)并发周期里重新标记的停顿;(4)Serial-Old收集老年代的停顿;(5)Full GC的停顿。其中并发模式失败会导致第(4)种情况,晋升失败和永久代空间耗尽会导致第(5)种情况。

2. 针对并发模式失败的调优

  • 想办法增大老年代的空间,增加整个堆的大小,或者减少年轻代的大小

  • 以更高的频率执行后台的回收线程,即提高CMS并发周期发生的频率。设置UseCMSInitiatingOccupancyOnly和CMSInitiatingOccupancyFraction参数,调低CMSInitiatingOccupancyFraction的值,但是也不能调得太低,太低了会导致过多的无效的并发周期,会导致消耗CPU时间和更多的无效的停顿。通常来讲,这个过程需要几个迭代,但是还是有一定的套路,参见《Java性能权威指南》中给出的建议,摘抄如下:

  • 对特定的应用程序,该标志的更优值可以根据 GC 日志中 CMS 周期首次启动失败时的值得到。具体方法是,在垃圾回收日志中寻找并发模式失效,找到后再反向查找 CMS 周期最近的启动记录,然后根据日志来计算这时候的老年代空间占用值,然后设置一个比该值更小的值。

    增多回收线程的个数 CMS默认的垃圾收集线程数是(CPU个数 + 3)/4,这个公式的含义是:当CPU个数大于4个的时候,垃圾回收后台线程至少占用25%的CPU资源。举个例子:如果CPU核数是1~4个,那么会有1个CPU用于垃圾收集,如果CPU核数是5~8个,那么久会有2个CPU用于垃圾收集。

  • 针对永久代的调优 如果永久代需要垃圾回收(或元空间扩容),就会触发Full GC。默认情况下,CMS不会处理永久代中的垃圾,可以通过开启CMSPermGenSweepingEnabled配置来开启永久代中的垃圾回收,开启后会有一组后台线程针对永久代做收集,需要注意的是,触发永久代进行垃圾收集的指标跟触发老年代进行垃圾收集的指标是独立的,老年代的阈值可以通过CMSInitiatingPermOccupancyFraction参数设置,这个参数的默认值是80%。开启对永久代的垃圾收集只是其中的一步,还需要开启另一个参数——CMSClassUnloadingEnabled,使得在垃圾收集的时候可以卸载不用的类。


四、CMS的trade-off是什么?

  1. 优势

  • 低延迟的收集器:几乎没有长时间的停顿,应用程序只在Minor gc以及后台线程扫描老年代的时候发生极其短暂的停顿。

劣势

  • 更高的CPU使用:必须有足够的CPU资源用于运行后台的垃圾收集线程,在应用程序线程运行的同时扫描堆的使用情况。【PS:现在服务器的CPU资源基本不是问题,这个点可以忽略】

  • CMS收集器对老年代收集的时候,不再进行任何压缩和整理的工作,意味着老年代随着应用的运行会变得碎片化;碎片过多会影响大对象的分配,虽然老年代还有很大的剩余空间,但是没有连续的空间来分配大对象,这时候就会触发Full GC。CMS提供了两个参数来解决这个问题:(1)UseCMSCompactAtFullCollection,在要进行Full GC的时候进行内存碎片整理;(2)CMSFullGCsBeforeCompaction,每隔多少次不压缩的Full GC后,执行一次带压缩的Full GC。

  • 会出现浮动垃圾;在并发清理阶段,用户线程仍然在运行,必须预留出空间给用户线程使用,因此CMS比其他回收器需要更大的堆空间。

五、几个问题的解答

  1. 为什么ParNew可以和CMS配合使用,而Parallel Scanvenge不可以? 答:这个跟Hotspot VM的历史有关,Parallel Scanvenge是不在“分代框架”下开发的,而ParNew、CMS都是在分代框架下开发的。



  2. CMS中minor gc和major gc是顺序发生的吗? 答:不是的,可以交叉发生,即在并发周期执行过程中,是可以发生Minor gc的,这个找个gc日志就可以观察到。



  3. CMS的并发收集周期合适触发? 由下图可以看出,CMS 并发周期触发的条件有两个:

    6cf94df0756378f83e794122ee6b19c94c57b7b1


  • 阈值检查机制:老年代的使用空间达到某个阈值,JVM的默认值是92%(jdk1.5之前是68%,jdk1.6之后是92%),或者可以通过CMSInitiatingOccupancyFraction和UseCMSInitiatingOccupancyOnly两个参数来设置;这个参数的设置需要看应用场景,设置得太小,会导致CMS频繁发生,设置得太大,会导致过多的并发模式失败。例如

  • 动态检查机制:JVM会根据最近的回收历史,估算下一次老年代被耗尽的时间,快到这个时间的时候就启动一个并发周期。设置UseCMSInitiatingOccupancyOnly这个参数可以将这个特性关闭。


CMS的并发收集周期会扫描哪些对象?会回收哪些对象? 答:CMS的并发周期只会回收老年代的对象,但是在标记老年代的存活对象时,可能有些对象会被年轻代的对象引用,因此需要扫描整个堆的对象。



CMS的gc roots包括哪些对象? 答:首先,在JVM垃圾收集中Gc Roots的概念如何理解(参见R大对GC roots的概念的解释);第二,CMS的并发收集周期中,如何判断老年代的对象是活着?我们前面提到了,在CMS的并发周期中,仅仅扫描Gc Roots直达的对象会有遗漏,还需要扫描新生代的对象。如下图中的蓝色字体所示,CMS中的年轻代和老年代是分别收集的,因此在判断年轻代的对象存活的时候,需要把老年代当作自己的GcRoots,这时候并不需要扫描老年代的全部对象,而是使用了card table数据结构,如果一个老年代对象引用了年轻代的对象,则card中的值会被设置为特殊的数值;反过来判断老年代对象存活的时候,也需要把年轻代当作自己的Gc Roots,这个过程我们在第三节已经论述过了。

f250905f2f0f3a159f43b0007340be8019387168


如果我的应用决定使用CMS收集器,推荐的JVM参数是什么?我自己的应用使用的参数如下,是根据PerfMa的xxfox生成的,大家也可以使用这个产品调优自己的JVM参数:
  1. -Xmx4096M-Xms4096M-Xmn1536M

  2. -XX:MaxMetaspaceSize=512M-XX:MetaspaceSize=512M

  3. -XX:+UseConcMarkSweepGC

  4. -XX:+UseCMSInitiatingOccupancyOnly

  5. -XX:CMSInitiatingOccupancyFraction=70

  6. -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses

  7. -XX:+CMSClassUnloadingEnabled

  8. -XX:+ParallelRefProcEnabled

  9. -XX:+CMSScavengeBeforeRemark

  10. -XX:ErrorFile=/home/admin/logs/xelephant/hs_err_pid%p.log

  11. -Xloggc:/home/admin/logs/xelephant/gc.log

  12. -XX:HeapDumpPath=/home/admin/logs/xelephant

  13. -XX:+PrintGCDetails

  14. -XX:+PrintGCDateStamps

  15. -XX:+HeapDumpOnOutOfMemoryError


CMS相关的参数总结(需要注意的是,这里我没有考虑太多JDK版本的问题,JDK1.7和JDK1.8这些参数的配置,有些默认值可能不一样,具体使用的时候还需要根据具体的版本来确认怎么设置)

bf70a4b790d34f5f7f3b223d42f8fef318e13ae5

参考资料

  1. 从实际案例聊聊Java应用的GC优化

  2. 理解CMS垃圾回收日志

  3. 图解CMS垃圾回收机制,你值得拥有

  4. 为什么CMS虽然是老年代的gc,但仍要扫描新生代的?

  5. R大对GC roots的概念的解释

  6. Introduce to CMS Collector

  7. 《深入理解Java虚拟机》

  8. 《Java性能权威指南》

  9. Oracle的GC调优手册

  10. what-is-the-threshold-for-cms-old-gc-to-be-triggered

  11. Frequently Asked Questions about Garbage Collection in the Hotspot Java VirtualMachine

  12. Java SE HotSpot at a Glance

  13. xxfox:PerfMa的参数调优神器

  14. 详解CMS垃圾回收机制

  15. ParNew和PSYoungGen和DefNew是一个东西么?

  16. Java SE的内存管理白皮书

原文发布时间为: 2018-12-03
本文作者:阿杜的世界
本文来自云栖社区合作伙伴“ Kirito的技术分享”,了解相关信息可以关注“Kirito的技术分享”。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/252891.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

相机工作原理

轻轻一按,你的相机就把光子转换为了比特。于是一张相片就保存到了你的 iPhone 里。 让我们假设一下你身处室外,环顾四周。三亿里之外,太阳无时无刻不在发射光子。它们需要花上 8 分钟之久才能到达我们舒适的星球。有一些光子撞击到你周围的物…

CentOS用户和用户组的操作

2019独角兽企业重金招聘Python工程师标准>>> CentOS用户和用户组的操作 長得太帥忚四種檌 关注 2018.05.12 16:40* 字数 312 阅读 115评论 0喜欢 0 用户组的操作 1.添加用户组: groupadd 组名2.修改组名 groupmod -n 新组名 原组名删除用户组groupdel 组…

Linux用户空间与内核地址空间

Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数据可能不…

浅谈微信smali注入

作者:郭少雷 搞android搞了几年也没搞出个啥牛逼app出来,眼看时下最火的app微信如此火热,实在想搞搞它,索性就想着给它加点东西进去。 以下内容纯属本人个人爱好,仅限个人学习android用途以及对android的深入了解。 首…

Embeded linux之移植iptables

一、内核环境&#xff1a; linux-3.4.35 -*- Networking support ---> Networking options ---> [*] Network packet filtering framework (Netfilter) ---> IP: Netfilter Configuration ---> <*> IP tables support (required for filtering/masq/NAT)…

Hadoop HIVE

数据仓库工具。构建在hadoop上的数据仓库框架&#xff0c;可以把hadoop下的原始结构化数据变成Hive中的表。&#xff08;主要解决ad-hoc query&#xff0c;即时查询的问题&#xff09; 支持一种与SQL几乎完全相同的语言HQL。除了不支持更新&#xff0c;索引和事务&#xff0c;几…

Xcode9学习笔记67 - 打印查看程序沙箱结构中常用的几个目录

override func viewDidLoad() {super.viewDidLoad()// Do any additional setup after loading the view, typically from a nib.//首先获得应用程序目录的路径&#xff0c;在该目录下有三个文件夹&#xff1a;文档目录、库目录、临时目录以及一个程序包。该目录就是应用程序的…

检测raid类型和磁盘坏道脚本

#!/bin/sh #脚本功能&#xff1a; #安装工具MegaCli64 #Host Information&#xff1a;主机名和ip地址 #Raid Information&#xff1a;raid信息和充电状态 #WARNING Information&#xff1a;MediaErrcount检测坏块和哪块盘 #Disk Information&#xff1a;磁盘信息 #上传MegaC…

《SpringBoot揭秘 快速构建微服务体系》读后感(三)

SpringApplication&#xff1a;SpringBoot程序启动的一站式解决方案 深入探索SpringApplication执行流程 因为书上的版本是1.2的&#xff0c;比较老&#xff0c;这里参考http://blog.csdn.net/zxzzxzzxz123/article/details/69941910 public ConfigurableApplicationContext ru…

装饰器函数

1.装饰器 ​ 装饰器&#xff1a;在不改变原函数的调用方式和函数&#xff0c;额外的增加功能 简单装饰器def timer(func):def inner():print(time.time())func() # 原来的函数return inner ​ timer # func1 timer(func1) def func1():print(func1) 函数带返回值def timer…

6G SDI/12G SDI 基带信号无压缩传输方案介绍

认知数字像素分辨率&#xff1a; 首先从分辨率(数字像素)角度来讲&#xff0c;从标清时代走到高清&#xff0c;从720x576到现在的1920x1080&#xff0c;宽高比从4:3到16:9&#xff0c;这个是我们比较熟悉的&#xff0c;4K实际上是建立在高清基础之上的&#xff0c;我们称之为“…

12.4日团队工作总结

今天团队的主要任务是注重于画图工具的设计&#xff0c;这就意味着我们首要的任务是将画图工具设置出来并可以完整运行&#xff0c;接下来才能顾及之前的改图软件&#xff0c;但今天在设计的过程中&#xff0c;遇到了两者无法无缝结合的问题&#xff0c;目前还没解决。 转载于:…

最短路径——Dijkstra算法以及二叉堆优化(含证明)

一般最短路径算法习惯性的分为两种&#xff1a;单源最短路径算法和全顶点之间最短路径。前者是计算出从一个点出发&#xff0c;到达所有其余可到达顶点的距离。后者是计算出图中所有点之间的路径距离。 单源最短路径 Dijkstra算法 思维 本质上是贪心的思想&#xff0c;声明一个…

linux shmget shmctl

shmgetint shmget(key_t key, size_t size, int flag);key: 标识符的规则size:共享存储段的字节数flag:读写的权限返回值&#xff1a;成功返回共享存储的id&#xff0c;失败返回-1key_t key----------------------------------------------- key标识共享内存的键值: 0/IPC_P…

伯克利开源工具库RLib现已支持大规模多智能体强化学习

AI前线导读&#xff1a;近日&#xff0c;UC伯克利的研究团队RISELab在其Github的项目Ray Rlib 0.6.0中添加了面向多智能体强化学习&#xff08;multi-agent Reinforcement Learning&#xff09;的支持。本文由团队成员Eric Liang首发于RISELab团队主页&#xff0c;AI前线翻译整…

如何简单理解光圈大小对手机摄影的影响?

你&#xff0c;准备好参加今夏的朋友圈摄影大赛了吗&#xff1f; 现在的天气有多热&#xff0c;谁出门谁知道&#xff01;出去玩还要背一台单反&#xff0c;绝对可以说是一种折磨了。但是&#xff0c;如果你拥有一台大光圈的手机&#xff0c;一样可以在朋友圈脱颖而出。 那么…

基于centos6.7的docker私有仓库搭建

2019独角兽企业重金招聘Python工程师标准>>> 1 仓库配置https认证 cd /etc/docker/ mkdir certs [rootdocker01 docker]# openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/docker01.key -x509 -days 365 -out certs/docker01.crt 填好相应的简称及email…

第十周软件工程作业-每周例行报告

一、PSP T名称C内容ST开始时间ED结束时间中断时间/min实际时间/min会议第一次Scrum会议11月17日16:0011月17日16:30030第二次Scrum会议11月18日15:0011月18日15:30030第三次Scrum会议11月19日17:0011月19日17:30030第四次Scrum会议11月20日11:3511月20日12:15040第五次Scrum会议…

MAVEN下载和安装

1.maven的下载 下载链接http://maven.apache.org/download.cgi从该网站下载最新版本 2.maven的安装 电脑上需要安装JDK环境&#xff0c;需要安装JDK7以上的版本。下载之后进行解压&#xff0c;将maven解压到不含中文和空格的一个目录 maven目录结构bin目录&#xff1a;mvn.bat、…

CCD/CMOS靶面尺寸型号标准

传感器尺寸指的是感光器对角线尺寸&#xff0c;1/1.7英寸&#xff08;14.8毫米&#xff0d;&#xff0d;导向管尺寸&#xff09;大于1/2.3英寸&#xff08;10.95毫米&#xff0d;&#xff0d;&#xff0d;导向管尺寸&#xff09;.采用同种技术水平的感光器&#xff0c;肯定是单…