在G1出来之前,CMS绝对是OLTP系统的标配。即使G1出来几年了,生产环境很多的JVM实例还是采用ParNew+CMS的组合。但是即使其得到这么广泛的应用,还是有很多同学对它有很深的误解。本文主要对ParNew+CMS经典组合下,触发的几种垃圾回收方式进行几个概念的纠正。

Backgroud CMS

可能更多人只知道CMS,而不知道Backgroud CMS。事实上我们说的CMS,即包含了5个阶段的CMS,就是Background CMS,如下图所示:

说明

  • 图中初始化标记阶段是串行的,这是JDK7的行为。JDK8以后默认是并行的,可以通过参数-XX:+CMSParallelInitialMarkEnabled控制。
  • 由图可知,CMS还有两个阶段是完全STW(Stop The World)的,即初始化标记和最终标记(重新标记)。
  • 其他阶段都是并发的,所以CMS被称为Concurrent Mark&Sweep,但是我认为前面还需要加个Mostly才是最贴切,即CMS是一个Mostly Concurrent Mark and Sweep Garbage Collector,因为它还没办法做到完全并发。

不只是CMS,就是G1,以及JDK11的ZGC都没有做到完全的并发。就目前笔者了解到的所有GC中,只有Azul的C4是完全并发的。

为什么有个Background关键词?我们都知道配置CMS垃圾回收的话,有两个重要参数:-XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly,这两个参数表示只有在Old区占了75%的内存时才满足触发CMS的条件。注意这只是满足触发CMS GC的条件。至于什么时候真正触发CMS GC,由一个后台扫描线程决定。CMSThread默认2秒钟扫描一次,判断是否需要触发CMS,这个参数可以更改这个扫描时间间隔,例如-XX:CMSWaitDuration=5000,此外可以通过jstack日志看到这个线程:

"Concurrent Mark-Sweep GC Thread" os_prio=2 tid=0x000000001870f800 nid=0x0f4 waiting on condition

Foregroud CMS

这个名词第一次听笨神说的。当然笨神也不是随便自己捏造一个名词出来,这个名词来自于openjdk源码,参考concurrentMarkSweepGeneration.cpp

void CMSCollector::collect_in_foreground(bool clear_all_soft_refs, GCCause::Cause cause) {case Resizing: {// nothing to be done in this state. 即这个阶段啥都没做_collectorState = Resetting;break;}  case Precleaning:// 预清理啥都没干case AbortablePreclean:// Elide(省略,取消的意思,相当于这个阶段也啥都没做) the preclean phase_collectorState = FinalMarking;break;default:ShouldNotReachHere();
}

源码比较多,我就不全部贴出来的,有兴趣的同学可以自己下载源码查看。

它发生的场景,比如业务线程请求分配内存,但是内存不够了,于是可能触发一次CMS GC,这个过程就必须要等待内存分配成功后业务线程才能继续往下面走,因此整个过程必须STW,所以这种CMS GC整个过程都是STW,但是为了提高效率,它并不是每个阶段都会走的,只走其中一些阶段,通过上面的源码可知,这些省下来的阶段主要是并行阶段:Precleaning、AbortablePreclean,Resizing。但不管怎么说如果走了类似foreground这种CMS GC,那么整个过程业务线程都是不可用的,效率会影响挺大。

这事实上就是发生了FullGC,由这段的分析可知FullGC相比CMS Backgroud collect模式差距还是非常大的。

MSC

MSC的全称是Mark Sweep Compact,即标记-清理-压缩,MSC是一种算法,请注意Compact,即它会压缩整理堆,这一点很重要。

这是foreground CMS在特定情况下才会采用的一种垃圾回收算法。为什么这么说呢,这里需要介绍两个参数,这两个参数表示多少次FullGC后采用MSC算法压缩堆内存,0表示每次FullGC后都会压缩,同时0也是默认值:

-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=0

配置-XX:+UseCMSCompactAtFullCollection(默认)前提下,如果CMSFullGCsBeforeCompaction=0,那么每次foreground CMS后都会采用MSC算法压缩堆内存;如果CMSFullGCsBeforeCompaction=3,那么每3次foreground CMS后才会有1次采用MSC算法压缩堆内存。

碎片问题也是CMS采用的标记清理算法最让人诟病的地方:Backgroud CMS采用的标记清理算法会导致内存碎片问题,从而埋下发生FullGC导致长时间STW的隐患

所以如果触发了FullGC,无论是否会采用MSC算法压缩堆,那都是ParNew+CMS组合非常糟糕的情况。因为这个时候并发模式已经搞不定了,而且整个过程单线程,完全STW,可能会压缩堆(是否压缩堆通过上面两个参数控制),真的不能再糟糕了!想象如果这时候业务量比较大,由于FullGC导致服务完全暂停几秒钟,甚至上10秒,对用户体验影响得多大。

另外,别以为G1就好很多,G1的FullGC同样是垃圾级别的存在:
The G1 garbage collector is designed to avoid full collections, but when the concurrent collections can’t reclaim memory fast enough a fall back full GC will occur. The current implementation of the full GC for G1 uses a single threaded mark-sweep-compact algorithm.

原文出自:http://openjdk.java.net/jeps/307

HOW?

FullGC这么恐怖,有办法缓解么,或者说尽量避免它在白天,甚至业务高峰期出现?有!笔者给你分享一个歪门邪道,不记得是多少年前,在哪里道听途说才得到这个偏方的,而且据说以前阿里的一些业务也用了这个偏方,不管是哪里得来的偏方,反正肯定有用的。这个偏方很简单:在业务最低峰期(比如大陆的很多业务可以选在凌晨2,3点夜深人静的时候)强行触发FullGC(需要结合参数-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0,这两个参数默认值就是这样的,表示触发FullGC时压缩堆),从而优化内存碎片并压缩堆,降低在业务高峰期发生FullGC的概率(只能降低,不能杜绝)。

可能还有一小部分同学连强行触发FullGC都不知道,笔者好人做到底,送佛送到西(也有一些同学给笔者留言说每天晚上重启,当然这也是可以减轻碎片化问题):

# 没有开启-XX:+DisableExplicitGC的前提下调用System.gc()就会发生FullGC
System.gc();或者通过jmap命令触发:
# jmap -histo:live pid

总结

按照惯例,最后来个总结:

  • 正常情况下触发Backgroud模式的CMS GC,这是并发模式收集,对业务影响很小,你好我好都好。
  • 当并发模式搞不定了,就会退化成Foreground模式,这个回收过程业务线程是不可用的,这时候就触发了FullGC。
  • 接下来根据上面提到的两个参数决定是否采用MSC算法压缩堆。
  • CMSFullGCsBeforeCompaction决定多少次FullGC后压缩堆,具体配置多大,由你决定,但是不建议太大,否则在采用MSC算法压缩堆之前,由于内存碎片的问题,导致出现promotion failure,总之这是trade-off。

友情提醒

  1. JVM很难,网上错误的观点很多;
  2. 再次推荐你假笨(公众号)和RednaxelaFX(只有知乎和ITEYE,江湖人称R大)。
  3. 友情链接:http://hllvm.group.iteye.com/group/topic/28854。

CMS几种GC模式解读相关推荐

  1. 互联网+教育:四种互联网教育新模式解读

    随着近几年互联网.移动互联网的高速发展,传统教育也顺应时势的与互联网结合,演化出了多种不同的在线教育场景. 互联网+教育:四种互联网教育新模式解读 " 互联网+教育 "的教育模式, ...

  2. 跨境电商的模式有哪些?4种高利润商业模式解读

    我是Dora,从事谷歌SEO超过10年时间,为7-Eleven.微软.腾讯等集团提供Google SEO营销顾问服务.点头像关注,有需要直接留言即可,感谢. 欲善其事,必先利其器.这道理在工作和生活中 ...

  3. GC日志解读,这次别再说看不懂GC日志了

    测试环境:机器内存16G,JDK 8,12核CPU 测试用例,从网上找的示例,视情况修改即可: import java.util.Random; import java.util.concurrent ...

  4. jdk8 cms g1gc_G1 vs CMS vs平行GC

    jdk8 cms g1gc 这篇文章是我们一年前进行的实验的跟进,比较了现实环境中不同GC算法的性能. 我们进行了相同的实验,将测试扩展为包含G1垃圾收集器,然后在不同的平台上运行了测试. 今年,我们 ...

  5. G1 vs CMS vs平行GC

    这篇文章是我们一年前进行的实验的跟进,比较了现实环境中不同GC算法的性能. 我们进行了相同的实验,将测试扩展为包含G1垃圾收集器,然后在不同的平台上运行了测试. 今年,我们的测试使用了以下垃圾收集器: ...

  6. 开关电源三种控制模式:PWM/PFM/PSM

    1. PWM/PFM/PSM 三种控制模式的定义 通常来说﹐开关电源(DC-DC)有三种最常见的调制方式分别为: 脉冲宽度调制(PWM) 脉冲频率调制(PFM) 脉冲跨周期调制(PSM) 在功率集成电 ...

  7. go语言和java比_Go VS Java:一位资深程序员对两种语言的解读

    导读:对于软件开发的编程语言,其实没有万能灵药. 本文作者详细介绍了他使用Java和Go这两种编程语言,一个是传统语言,一个是新兴语言的工作方式. Go VS Java 实话说,我很喜欢Java这门语 ...

  8. 扬帆志远教育:对跨境电商商业模式解读

    随着全球经济联系的日益紧密,跨境电商行业应运而生,跨境电商为人们的生活提供了巨大的便利的同时也反过来带动了其他行业的发展,比如国际物流业.跨境电子商务的发展趋势是物流.信息流和现金流的协调发展的结果. ...

  9. 带你一文搞懂VMware Workstation的三种网络模式

    前言: 其实VMware Workstation的三种网络模式我学的不止一遍,但每次学习自己理解的都是朦胧的概念,说自己学会了吧,一些实质性的概念转头就忘记,一点也想不起来.说自己不会吧,但是每次学习 ...

最新文章

  1. python打包为exe文件_Pyinstaller(python打包为exe文件)
  2. GARFIELD@01-18-2005
  3. [Nova ERROR] InternalError: Nova requires QEMU version 2.5.0 or greater.
  4. idea mybatis generator插件_Mybatis使用自定义插件去掉POJO的Getter和Setter方法
  5. python的scrapy爬虫模块间进行传参_Python | Scrapy 爬虫过程问题解决(持续更新...)...
  6. oracle中nvl()函数
  7. CentOS 7从Python 2.7升级至Python3.6.1
  8. 【CodeForces 577C】Vasya and Petya’s Game
  9. [原创]java WEB学习笔记35:java WEB 中关于绝对路径 和相对路径问题
  10. 语录:101条伟大的计算机编程名言
  11. 有哪些特点_锌钢栅栏有哪些特点?锌钢栅栏特点有哪些?
  12. three 实现绕物体旋转,卫星绕星球旋转
  13. Windows Qt设置环境变量
  14. idea2017 破解版使用
  15. 免费云服务器(阿贝云服务器入门)(仅适用于windows server系统)
  16. 读nandflash---根据数据手册K9GAG08U0D
  17. flutter 学习之项目一
  18. 蓝牙(三)蓝牙协议的初始化
  19. redis排查读取超时、连接超时
  20. android 新闻功能列表,news: 用Android基本技术实现的新闻App

热门文章

  1. android 音量调节框,Android 音量调节方法
  2. 高视角!如何认识微积分方法与原理?
  3. 从计算机视觉角度深入解读了AR
  4. Flask框架详细教程
  5. SVN版本控制与恢复删除
  6. 超过4G如何制作NTFS格式WINPE?
  7. 新浪微博视频批量上传社区投稿工具教程
  8. python实现自动登录qq邮箱,写邮件并发送
  9. 你真的会用搜索引擎吗?能写出好论文、找到好工作的那种
  10. 软件行业的发展要尊重软件工程的价值规律