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日志)

目录
相关文章
|
监控 算法 Java
Java虚拟机(JVM)的垃圾回收机制深度解析####
本文深入探讨了Java虚拟机(JVM)的垃圾回收机制,旨在揭示其背后的工作原理与优化策略。我们将从垃圾回收的基本概念入手,逐步剖析标记-清除、复制算法、标记-整理等主流垃圾回收算法的原理与实现细节。通过对比不同算法的优缺点及适用场景,为开发者提供优化Java应用性能与内存管理的实践指南。 ####
|
安全 Oracle Java
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
775 0
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
|
存储 Java 开发者
浅析JVM方法解析、创建和链接
上一篇文章《你知道Java类是如何被加载的吗?》分析了HotSpot是如何加载Java类的,本文再来分析下Hotspot又是如何解析、创建和链接类方法的。
743 132
|
监控 算法 Java
JVM—垃圾收集算法和HotSpot算法实现细节
JVM的垃圾收集算法和HotSpot的实现细节复杂但至关重要,通过理解和掌握这些算法,可以为Java应用程序选择合适的垃圾收集器,并进行有效的性能调优。选择适当的垃圾收集策略,结合合理的内存配置和日志分析,能够显著提升应用的运行效率和稳定性。
387 15
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
Java 编译器 API
深入解析:JDK与JVM的区别及联系
在Java开发和运行环境中,JDK(Java Development Kit)和JVM(Java Virtual Machine)是两个核心概念,它们在Java程序的开发、编译和运行过程中扮演着不同的角色。本文将深入解析JDK与JVM的区别及其内在联系,为Java开发者提供清晰的技术干货。
516 1
JVM进阶调优系列(5)CMS回收器通俗演义一文讲透FullGC
本文介绍了JVM中CMS垃圾回收器对Full GC的优化,包括Stop the world的影响、Full GC触发条件、GC过程的四个阶段(初始标记、并发标记、重新标记、并发清理)及并发清理期间的Concurrent mode failure处理,并简述了GC roots的概念及其在GC中的作用。
|
算法 Java
JVM有哪些垃圾回收算法?
(1)标记清除算法: 标记不需要回收的对象,然后清除没有标记的对象,会造成许多内存碎片。 (2)复制算法: 将内存分为两块,只使用一块,进行垃圾回收时,先将存活的对象复制到另一块区域,然后清空之前的区域。用在新生代 (3)标记整理算法: 与标记清除算法类似,但是在标记之后,将存活对象向一端移动,然后清除边界外的垃圾对象。用在老年代
257 0
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
961 140
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
1597 29

热门文章

最新文章

推荐镜像

更多
  • DNS