本文翻译自:
https://blogs.oracle.com/poonam/entry/understanding_cms_gc_logs

准备工作

JVM的GC日志的主要参数包括如下几个:
-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径

CMS GC知识

CMS,全称Concurrent Mark and Sweep,用于对年老代进行回收,目标是尽量减少应用的暂停时间,减少full gc发生的机率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代。
CMS并非没有暂停,而是用两次短暂停来替代串行标记整理算法的长暂停,内外的设置正常收集周期是这样的:

  1、CMS-initial-mark 初始标记
  2、CMS-concurrent-mark 并发标记的
  3、CMS-concurrent-preclean 执行预清理
  4、CMS-concurrent-abortable-preclean 执行可中止预清理
  5、CMS-remark 重新标记
  6、CMS-concurrent-sweep 并发清除
  7、CMS-concurrent-reset 并发重设状态等待下次CMS的触发

其中,CMS-initial-mark和CMS-remark会stop-the-world。

具体CMS的原理可以参看这篇文章
了解CMS(Concurrent Mark-Sweep)垃圾回收器

理解CMS GC日志

启动jvm的时候,增加参数-XX:+PrintGCDetails 和 -XX:+PrintGCTimeStamps可以打印出CMS GC的详细日志。读懂log信息有助于应用系统的各种参数调优,同时也有助于使得CMS实现更好的性能。

下面的例子使用了1.8的jvm

2016-12-21T15:05:30.175+0800: 90.439: [GC2016-12-21T15:05:30.175+0800: 90.439: [ParNew: 720896K->49062K(720896K), 0.2289360 secs] 864443K->262770K(1507328K), 0.2292340 secs] [Times: user=0.42 sys=0.00, real=0.23 secs]

Young generation(ParNew)收集。新生代的容量是720896K,在使用量达到了720896K时发生了GC,回收的Young generation容量为49062K。本次GC花费了0.2289360s

2016-12-21T15:05:14.628+0800: 74.892: [GC [1 CMS-initial-mark: 646746K(786432K)] 723228K(1507328K), 0.0456000 secs] [Times: user=0.04 sys=0.00, real=0.04 secs]

CMS开始回收tenured generation collection。这阶段是CMS初始化标记的阶段,从垃圾回收的“根对象”开始,且只扫描直接与“根对象”直接关联的对象,并做标记,在此期间,其他线程都会停止。

tenured generation的空间是786432K,在容量为646746K时开始执行初始标记。

2016-12-21T15:05:14.674+0800: 74.938: [CMS-concurrent-mark-start]

Concurrent marking(并发标记)阶段开始。
本阶段,其他线程重新开始正常执行,在上一步初始对象的基础上继续向下追溯标记

2016-12-21T15:05:15.188+0800: 75.452: [CMS-concurrent-mark: 0.513/0.514 secs] [Times: user=0.95 sys=0.05, real=0.52 secs]

并发标记阶段发费了0.513s cpu time 和0.514s 系统时间(包括其他线程占用cpu导致标记线程挂起的时间)

2016-12-21T15:05:15.188+0800: 75.452: [CMS-concurrent-preclean-start]

concurrent-preclean(并发域清理)阶段开始
并发预清理阶段仍然是并发的。在这个阶段,虚拟机查找在执行并发标记阶段新进入老年代的对象(可能会有一些对象从新生代晋升到老年代, 或者有一些对象被分配到老年代)。通过重新扫描,减少下一个阶段"重新标记"的工作,因为下一个阶段会Stop The World。

2016-12-21T15:05:15.192+0800: 75.456: [CMS-concurrent-preclean: 0.004/0.004 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]

concurrent-preclean花费了0.004s cpu time 和 0.004s 系统时间

2016-12-21T15:05:20.256+0800: 80.521: [GC[YG occupancy: 377234 K (720896 K)]2016-12-21T15:05:20.256+0800: 80.521: [Rescan (parallel) , 0.3789280 secs]2016-12-21T15:05:20.635+0800: 80.900: [weak refs processing, 0.0017780 secs]2016-12-21T15:05:20.637+0800: 80.901: [class unloading, 0.0141220 secs]2016-12-21T15:05:20.651+0800: 80.916: [scrub symbol table, 0.0259990 secs]2016-12-21T15:05:20.677+0800: 80.942: [scrub string table, 0.0024140 secs] [1 CMS-remark: 646746K(786432K)] 1023980K(1507328K), 0.4268320 secs] [Times: user=0.78 sys=0.01, real=0.42 secs]

rescan阶段,会暂停其他线程。重新扫描CMS堆中剩余的对象,重新从“根对象”开始扫描,并且也会处理对象关联。本次扫描花费了 0.3789280s,其中弱引用对象(weak referene objects)处理大约用来0.0017780s,本阶段共计时间0.4268320s

2016-12-21T15:05:20.685+0800: 80.950: [CMS-concurrent-sweep-start]

开始清理没有标记的对象,清理阶段是和其他线程并发进行的。

2016-12-21T15:05:21.340+0800: 81.605: [CMS-concurrent-sweep: 0.640/0.655 secs] [Times: user=1.23 sys=0.03, real=0.66 secs]

时间,不解释了

2016-12-21T15:05:21.341+0800: 81.605: [CMS-concurrent-reset-start]
2016-12-21T15:05:21.402+0800: 81.666: [CMS-concurrent-reset: 0.061/0.061 secs] [Times: user=0.06 sys=0.05, real=0.06 secs]

reset 阶段开始,并且输出所用时间。在这个阶段,与CMS相关数据结构被重新初始化,这样下一个周期可以正常进行。

以上过程是一个正常的CMS GC循环周期,接下来再分析一些不正常的日志

以下这个情况我没有复现,用的是文章的中数据,TODO之后想想能否打印出来这种情况

197.976: [GC 197.976: [ParNew: 260872K->260872K(261952K), 0.0000688 secs]197.976: [CMS197.981: [CMS-concurrent-sweep: 0.516/0.531 secs]
(concurrent mode failure): 402978K->248977K(786432K), 2.3728734 secs] 663850K->248977K(1048384K), 2.3733725 secs]

这显示,ParNew收集请求执行,但是没有成功。因为此时系统估计没有老生代中没有足够的空间去容纳这些对象(预测之后可能会出现老生代的空余空间将会被系统占光),我们称这种情况为 “full promotion guarantee failure”

在这种情况下下,并发式的CMS被阻塞了,full GC执行了,GC算法进入了concurrent mode failure状态,调用一个serail Old GC(阻塞了其他线程)来清理系统的Heap

日志显示,Full GC花费了2.3733725s,老生代空间由402978K降到了248977K

concurrent mode failure可以通过增大老生代的空间或者通过设置CMSInitiatingOccupancyFraction一个小的值使得CMS Collection发生的更频繁(CMSInitiatingOccupancyFraction可以控制CMS执行的时间,假设设置为70,说明老生代在利用率为70%时发生CMS),但是把这个值设置小也会导致CMS发生更加频繁。

某些情况下,promotion failures也会发生,即使是老生代有足够的空间。这个原因是"fragmentation"-老生代的可用空间不是连续的,而将新生代的对象移动到老生代需要连续的可用空间。而CMS是不会对内存进行压缩的算法,因此造成了这种问题。TODO,这篇文章解决了这个问题,我还没读

补充一下:concurrent mode failure产生的原理:CMS并发处理阶段用户线程还在运行中,伴随着程序运行会有新的垃圾产生,CMS无法处理掉它们(没有标记),只能在下一次GC的时候处理。同样的,用户线程运行就需要分配新的内存空间,为此,CMS收集器并不会在老年代全部被填满以后在进行收集,会预留一部分空间提供并发收集时的程序运行使用。即使是这样,还是会存在CMS运行期间预留的内存无法满足程序需求,就会出现"Concurrent Mode Failure"失败,这是,虚拟机将会启动备案操作:临时启动Serial Old 收集器来重新进行老年代的垃圾收集,Serial Old收集器会Stop the world,这样会导致停顿时间过长

同样的,CMS收集结束后会有大量的碎片空间差生,也会给大对象分配带来麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够多的连续空间来分配当前对象,不得不提前触发一次Full GC

197.976: [GC 197.976: [ParNew: 260872K->260872K(261952K), 0.0000688 secs]197.976: [CMS197.981: [CMS-concurrent-sweep: 0.516/0.531 secs] (concurrent mode failure): 402978K->248977K(786432K), 2.3728734 secs] 663850K->248977K(1048384K), 2.3733725 secs]

这行GC日志显示的是一个请求失败的新生代GC处理。因为没有足够的空间来存储由新生代晋升上来的对象。这种现象称之为full promotioin guarantee failure 就此会在197.981处产生一次FULL GC,花费了 2.3733725秒 ,从而使CMS的空间从 402978K->248977K.

理解CMS GC日志相关推荐

  1. JVM调优——之CMS GC日志分析

    最近在学习JVM和GC调优,今天总结下CMS的一些特点和要点,让我们先简单的看下整个堆年轻代和年老代的垃圾收集器组合(以下配合java8完美支持,其他版本可能稍有不同),其中标红线的则是我们今天要着重 ...

  2. 在linux上gc日志详解,JVM CMS GC日志详解

    # JDK8 -Xms20M -Xmx20M -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC CMS 日志格式: [GC (Allocation Failure ...

  3. 0 full gc时cpu idle_结合GC日志讲讲CMS垃圾收集器

    1 CMS垃圾收集器介绍 CMS(Concurrent Mark Sweep)收集器旨在获取最短回收停顿时间的并发垃圾收集器.CMS基于"标记-清除"算法实现,并发指的是CMS的垃 ...

  4. java 内存日志_JVM深入理解-内存调优与GC日志

    CPU飚高分析 一般可以使用 ps -Lfp pid ps -mp pid -o THREAD, tid, time top -Hp pid [root@redis webapps]# top -Hp ...

  5. JVM GC 日志详解

    本文采用的JDK版本: java version "1.8.0_144" Java(TM) SE Runtime Environment (build 1.8.0_144-b01) ...

  6. JVM发生频繁 CMS GC,罪魁祸首是这个参数!

    了解 CMS GC 的同学,一定知道 -XX:CMSScavengeBeforeRemark 参数,它是用来开启或关闭在 CMS-remark 阶段之前的清除(Young GC)尝试. 大家都知道CM ...

  7. 一个 JVM 参数引发的频繁 CMS GC

    了解 CMS GC 的同学,一定知道 -XX:CMSScavengeBeforeRemark 参数,它是用来开启或关闭在 CMS-remark 阶段之前的清除(Young GC)尝试. 大家都知道CM ...

  8. GC日志详解[乐乐独记]

    GC日志详解[乐乐独记] 1.静态分析GC日志 1.1.Parallel GC日志 1.2.CMS GC日志 1.3.G1 GC日志 2.使用工具分析GC日志 10.辅助知识 10.1.元空间不足也会 ...

  9. java gc日志详解_JVM GC 日志详解

    本文采用的JDK版本: java version "1.8.0_144" Java(TM) SE Runtime Environment (build 1.8.0_144-b01) ...

最新文章

  1. R语言ggplot2可视化绘制多条基本线图(Basic line plot)、使用gghighlight包突出高亮其中的某一条线图(highlight line plot)
  2. 传统自定义标签(2)-Tag接口的执行流程(包含执行顺序的图片)
  3. java基础--相等
  4. vue中的倒计时跳转页面问题和axios网络请求this作用域问题
  5. 半波对称振子方向图_移动天线的概念 | 天线方向性
  6. 软件测试中的 QM、QA、QC、测试规程、测试用例
  7. Jquery调用WCF经验分享
  8. 【毕业设计】基于stm32的万能红外遥控器 - 单片机 嵌入式 物联网
  9. Druid SQL注入检测
  10. 探索式测试方法的实践
  11. EPLAN教程——端子图表端子数量超出图框了怎么办
  12. c语言中的加减乘除字母,C 语言简单加减乘除运算
  13. 如何用控制台cmd打开你的c++程序
  14. 服务器安装m.2固态硬盘,笔记本怎么安装M.2接口固态硬盘?笔记本M.2接口固态硬盘安装方法...
  15. [Git[ 解决 git branch -M main error: refname refs/heads/master not found
  16. 解压后在别的电脑能安装PS,在我电脑就提示安装文件损坏
  17. arcgis10之将一个shp属性数据通过某一字段将要素属性关联至另一shp文件中
  18. 国庆怎么玩?国庆去面试!
  19. Microsoft Project
  20. 蓝牙芯片|伦茨科技-智能蓝牙BLE5.2芯片ST17H66

热门文章

  1. VRRP——虚拟路由器冗余协议
  2. arcgis导入excel数据_ArcGIS批量导入数据
  3. git add后取消_Python 命令行之旅:使用 click 实现 git 命令
  4. Java与C底层数据类型转换
  5. #ifndef #define #endif ”防止头文件被重复包 .
  6. win10远端服务器未响应,win10怎么连远程服务器未响应
  7. python 无法引用 tensorflow.keras_win10+anaconda安装tensorflow和keras遇到的坑小结
  8. 字符串操作 c语言,C语言字符串操作(示例代码)
  9. php文本分割成csv,php将文本文件转换csv输出的方法
  10. 我的世界连锁挖矿下载JAVA_我的世界1.7.10连锁挖矿整合包