JVM CMS GC算法解析

简介: JVM CMS GC算法解析

CMS,全称Concurrent Low Pause Collector,是jdk1.4后期版本开始引入的新gc算法,在jdk5和jdk6中得到了进一步改进,它的主要适合场景是对响应时间的重要性需求 大于对吞吐量的要求,能够承受垃圾回收线程和应用线程共享处理器资源,并且应用中存在比较多的长生命周期的对象的应用。CMS是用于对Old+Perm回收,采用CMS时候,新生代必须使用Serial GC或者ParNew GC两种。目标是尽量减少应用的暂停时间,减少Full   GC发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代。作为ARPG游戏服务器的应用,因为有永久缓存的存在,并且对于玩家操作响应时间也有非常高的要求(12+W/s 数据包),因此希 望能使用CMS来替代默认的server型JVM使用的并行收集器(Parallel MSC),以便获得更短的垃圾回收的暂停时间,提高程序的响应性。
CMS收集周期
CMS并非没有暂停,而是用两次短暂停来替代串行标记整理算法的长暂停,它的收集周期是这样:
初始标记(CMS-initial-mark) -> 并发标记(CMS-concurrent-mark) ->并发预清理(concurrent pre-clean)-> 重新标记(CMS-remark) -> 并发清除(CMS-concurrent-sweep) ->并发重设状态等待下次CMS的触发(CMS-concurrent-reset)。
其中的1,4两个步骤需要暂停所有的应用程序线程的。
第一次暂停从root对象开始标记存活的对象,这个阶段称为初始标记;第二次暂停是在并发标记之后,暂停所有应用程序线程,重新标记并发标记阶段遗漏的对象(在并发标记阶段结束后对象状态的更新导致)。第一次暂停会比较短,第二次暂停通常会比较长,并且 remark这个阶段可以并行标记。
而并发标记、并发清除、并发重设阶段的所谓并发,是指一个或者多个垃圾回收线程和应用程序线程并发地运行,垃圾回收线程不会暂停应用程序的执行,如果你有多于一个处理器,那么并发收集线程将与应用线程在不同的处理器上运行,显然,这样的开销就是会降低应用的吞吐量。Remark阶段的并行,是指暂停了所有应用程序后,启动一定数目的垃圾回收进程进行并行标记,此时的应用线程是暂停的。
CMS的young generation的回收采用的仍然是并行复制收集器,这个跟Parallel gc算法是一致的。


一段正常的CMS的日志,CMS的周期一般包含以下步骤:

========初始标记(CMS-initial-mark)============
2015-10-10T19:22:19.472+0800: 904337.364: [GC [1 CMS-initial-mark: 1996330K(2124800K)] 2089808K(3270400K), 0.0667700 secs] [Times: user=0.06 sys=0.00, real=0.07 secs] 
各个数据依次表示标记前后old区的所有对象占内存大小[1996330K],old的capacity[2124800K],整个JavaHeap(不包括perm)所有对象占内存总的大小[2089808K],JavaHeap的capacity[3270400K]。
2015-10-10T19:22:19.538+0800: 904337.430: Total time for which application threads were stopped: 0.0674020 seconds
========并发标记(CMS-concurrent-mark)=========
2015-10-10T19:22:19.538+0800: 904337.430: [CMS-concurrent-mark-start]
2015-10-10T19:22:19.540+0800: 904337.432: Total time for which application threads were stopped: 0.0010340 seconds
2015-10-10T19:22:21.471+0800: 904339.363: [CMS-concurrent-mark: 1.931/1.932 secs] [Times: user=6.94 sys=0.30, real=1.93 secs]
========并发预清理(concurrent pre-clean)======
2015-10-10T19:22:21.471+0800: 904339.363: [CMS-concurrent-preclean-start]
2015-10-10T19:22:21.608+0800: 904339.500: [CMS-concurrent-preclean: 0.117/0.137 secs] [Times: user=0.15 sys=0.06, real=0.13 secs] 
2015-10-10T19:22:21.608+0800: 904339.500: [CMS-concurrent-abortable-preclean-start]CMS: abort preclean due to time 
2015-10-10T19:22:26.798+0800: 904344.690: [CMS-concurrent-abortable-preclean: 5.140/5.190 secs] [Times: user=7.10 sys=1.45, real=5.19 secs] 
=========重新标记(CMS-remark)=================
2015-10-10T19:22:26.799+0800: 904344.691: [GC[YG occupancy: 270973 K (1145600 K)]
2015-10-10T19:22:26.799+0800: 904344.691: [Rescan (parallel) , 0.2171280 secs]
2015-10-10T19:22:27.016+0800: 904344.908: [weak refs processing, 0.0000700 secs]
2015-10-10T19:22:27.016+0800: 904344.908: [scrub string table, 0.0016420 secs] [1 CMS-remark: 1996330K(2124800K)] 2267303K(3270400K), 0.2190720 secs] [Times: user=2.13 sys=0.00, real=0.22 secs] 
2015-10-10T19:22:27.021+0800: 904344.913: Total time for which application threads were stopped: 0.2232820 seconds
==========并发清除(CMS-concurrent-sweep)======
2015-10-10T19:22:27.022+0800: 904344.914: [CMS-concurrent-sweep-start]
2015-10-10T19:22:27.970+0800: 904345.862: [CMS-concurrent-sweep: 0.948/0.948 secs] [Times: user=1.53 sys=0.18, real=0.95 secs]
==========并发重设状态(CMS-concurrent-reset)==
2015-10-10T19:22:27.970+0800: 904345.862: [CMS-concurrent-reset-start]
2015-10-10T19:22:27.976+0800: 904345.868: [CMS-concurrent-reset: 0.005/0.005 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]





其中可以看到CMS-initial-mark阶段暂停了0.0674020 seconds,而CMS-remark阶段暂停了0.2232820 seconds,因此两次暂停的总共时间是0.290684seconds,也就是290毫秒左右。两次短暂停的时间之和在300ms以下可以称为正常现象。



啥时候会触发CMS GC?



1、旧生代或者持久代已经使用的空间达到设定的百分比时(默认CMS是在tenured generation,也就是old区占满92%的时候开始进行CMS收集,

2、JVM自动触发(JVM的动态策略,也就是悲观策略)(基于之前GC的频率以及旧生代的增长趋势来评估决定什么时候开始执行),如果不希望JVM自行决定,可以通过-XX:UseCMSInitiatingOccupancyOnly=true来制定;

CMS GC因为是在程序运行时进行GC,不会暂停,所以不能等到不够用的时候才去开启GC,官方说法是他们的默认值是68%,但是可惜的是文档写错了,经过很多测试和源码验证这个参数应该是在92%的时候被启动,

-XX:CMSInitiatingOccupancyFraction=70

这样保证Old的内存在使用到70%的时候,就开始启动CMS了;如果你真的想看看默认值,那么就使用参数:-XX:+PrintCMSInitiationStatistics 这个变量只有JDK 1.6可以使用 1.5不可以,查看实际值-XX:+PrintCMSStatistics



reference:

The default for JVM 1.4.2 is 68%, but do NOT assume that the JVM always uses the default if you do not specify this parameter. Instead, analyze the GC logs to understand when the JVM triggers a CMS GC. we determined that a lot of objects were promoted to the tenured generation and CMS GC was mostly triggered at 50% occupancy of the tenured generation with some occurrences between 60% to 75% also


为啥CMS会有内存碎片,如何避免?

ygc变慢的原因通常是由于old gen出现了大量的碎片。

由于在CMS的回收步骤中,没有对内存进行压缩,所以会有内存碎片出现,CMS提供了一个整理碎片的功能,通过-XX:UseCMSCompactAtFullCollection 来启动此功能,启动这个功能后,默认每次执行Full GC的时候会进行整理,目前默认就是true了!(也可以通过-XX:CMSFullGCsBeforeCompaction =n来制定多少次Full GC之后来执行整理),整理碎片会stop-the-world.默认是0,也就是在默认配置下每次CMS GC顶不住了而要转入full GC的时候都会做压缩。  



啥时候会触发Full GC?

一、旧生代空间不足:java.lang.outOfMemoryError:java heap space;

二、Perm空间满:java.lang.outOfMemoryError:PermGen space;

三、CMS GC时出现promotion failed  和concurrent  mode failure(Concurrent mode failure发生的原因一般是CMS正在进行,但是由于old区内存不足,需要尽快回收old区里面的死的java对象,这个时候foreground gc需要被触发,停止所有的java线程,同时终止CMS,直接进行MSC。);

四、统计得到的minor GC晋升到旧生代的平均大小大于旧生代的剩余空间;

五、主动触发Full GC(执行jmap -histo:live [pid])来避免碎片问题;或者System.gc();



什么时候不应该用CMS?

heap小于3g不建议使用CMS GC



参考文章:

一次CMS GC问题排查过程(理解原理+读懂GC日志)

目录
相关文章
|
1月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
2月前
|
机器学习/深度学习 人工智能 搜索推荐
从零构建短视频推荐系统:双塔算法架构解析与代码实现
短视频推荐看似“读心”,实则依赖双塔推荐系统:用户塔与物品塔分别将行为与内容编码为向量,通过相似度匹配实现精准推送。本文解析其架构原理、技术实现与工程挑战,揭秘抖音等平台如何用AI抓住你的注意力。
558 7
从零构建短视频推荐系统:双塔算法架构解析与代码实现
|
2月前
|
机器学习/深度学习 存储 算法
动态规划算法深度解析:0-1背包问题
0-1背包问题是经典的组合优化问题,目标是在给定物品重量和价值及背包容量限制下,选取物品使得总价值最大化且每个物品仅能被选一次。该问题通常采用动态规划方法解决,通过构建二维状态表dp[i][j]记录前i个物品在容量j时的最大价值,利用状态转移方程避免重复计算子问题,从而高效求解最优解。
442 1
|
2月前
|
算法 搜索推荐 Java
贪心算法:部分背包问题深度解析
该Java代码基于贪心算法求解分数背包问题,通过按单位价值降序排序,优先装入高价值物品,并支持部分装入。核心包括冒泡排序优化、分阶段装入策略及精度控制,体现贪心选择性质,适用于可分割资源的最优化场景。
258 1
贪心算法:部分背包问题深度解析
|
2月前
|
机器学习/深度学习 边缘计算 人工智能
粒子群算法模型深度解析与实战应用
蒋星熠Jaxonic是一位深耕智能优化算法领域多年的技术探索者,专注于粒子群优化(PSO)算法的研究与应用。他深入剖析了PSO的数学模型、核心公式及实现方法,并通过大量实践验证了其在神经网络优化、工程设计等复杂问题上的卓越性能。本文全面展示了PSO的理论基础、改进策略与前沿发展方向,为读者提供了一份详尽的技术指南。
粒子群算法模型深度解析与实战应用
|
2月前
|
机器学习/深度学习 资源调度 算法
遗传算法模型深度解析与实战应用
摘要 遗传算法(GA)作为一种受生物进化启发的优化算法,在复杂问题求解中展现出独特优势。本文系统介绍了GA的核心理论、实现细节和应用经验。算法通过模拟自然选择机制,利用选择、交叉、变异三大操作在解空间中进行全局搜索。与梯度下降等传统方法相比,GA不依赖目标函数的连续性或可微性,特别适合处理离散优化、多目标优化等复杂问题。文中详细阐述了染色体编码、适应度函数设计、遗传操作实现等关键技术,并提供了Python代码实现示例。实践表明,GA的成功应用关键在于平衡探索与开发,通过精心调参维持种群多样性同时确保收敛效率
机器学习/深度学习 算法 自动驾驶
469 0
|
2月前
|
机器学习/深度学习 人工智能 资源调度
大语言模型的核心算法——简要解析
大语言模型的核心算法基于Transformer架构,以自注意力机制为核心,通过Q、K、V矩阵动态捕捉序列内部关系。多头注意力增强模型表达能力,位置编码(如RoPE)解决顺序信息问题。Flash Attention优化计算效率,GQA平衡性能与资源消耗。训练上,DPO替代RLHF提升效率,MoE架构实现参数扩展,Constitutional AI实现自监督对齐。整体技术推动模型在长序列、低资源下的性能突破。
377 8
|
2月前
|
算法 API 数据安全/隐私保护
深度解析京东图片搜索API:从图像识别到商品匹配的算法实践
京东图片搜索API基于图像识别技术,支持通过上传图片或图片URL搜索相似商品,提供智能匹配、结果筛选、分页查询等功能。适用于比价、竞品分析、推荐系统等场景。支持Python等开发语言,提供详细请求示例与文档。
|
3月前
|
安全 Oracle Java
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
303 0
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡

热门文章

最新文章

推荐镜像

更多
  • DNS