美文网首页
记一次Fullcut频发

记一次Fullcut频发

作者: 少博先生 | 来源:发表于2018-04-21 01:03 被阅读0次

上周系统出现了FullGC频发的问题,应该是内存中又出现了大对象导致的,到底是分页查询的pageSize=10000导致的呢还是存在别的什么原因?

GC日志

19:26开始FullGC一直持续到19:40
2018-04-18T19:21:50.142+0800: 433042.760: [GC2018-04-18T19:21:50.142+0800: 433042.760: [ParNew: 631407K->2100K(707840K), 0.0056650 secs] 959391K->330123K(2018560K), 0.0059200 secs] [Times: user=0.09 sys=0.00, real=0.00 secs] 
2018-04-18T19:24:03.688+0800: 433176.306: [GC2018-04-18T19:24:03.688+0800: 433176.306: [ParNew: 631348K->78592K(707840K), 0.3269520 secs] 959371K->544673K(2018560K), 0.3272060 secs] [Times: user=1.74 sys=0.06, real=0.33 secs] 
2018-04-18T19:24:05.109+0800: 433177.728: [GC2018-04-18T19:24:05.109+0800: 433177.728: [ParNew: 707840K->78592K(707840K), 0.3398880 secs] 1173921K->730279K(2018560K), 0.3401420 secs] [Times: user=3.75 sys=0.00, real=0.34 secs] 
2018-04-18T19:24:06.655+0800: 433179.273: [GC2018-04-18T19:24:06.655+0800: 433179.273: [ParNew: 707840K->78592K(707840K), 0.2906480 secs] 1359527K->894638K(2018560K), 0.2909050 secs] [Times: user=3.83 sys=0.00, real=0.29 secs] 
2018-04-18T19:24:06.946+0800: 433179.565: [GC [1 CMS-initial-mark: 816046K(1310720K)] 903563K(2018560K), 0.0997080 secs] [Times: user=0.10 sys=0.00, real=0.10 secs] 
2018-04-18T19:24:07.046+0800: 433179.665: [CMS-concurrent-mark-start]
2018-04-18T19:24:07.300+0800: 433179.918: [CMS-concurrent-mark: 0.254/0.254 secs] [Times: user=1.89 sys=0.00, real=0.26 secs] 
2018-04-18T19:24:07.300+0800: 433179.919: [CMS-concurrent-preclean-start]
2018-04-18T19:24:07.314+0800: 433179.932: [CMS-concurrent-preclean: 0.013/0.013 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
2018-04-18T19:24:07.314+0800: 433179.932: [CMS-concurrent-abortable-preclean-start]
2018-04-18T19:24:09.876+0800: 433182.494: [CMS-concurrent-abortable-preclean: 2.006/2.562 secs] [Times: user=7.38 sys=0.23, real=2.56 secs] 
2018-04-18T19:24:09.877+0800: 433182.495: [GC[YG occupancy: 692704 K (707840 K)]2018-04-18T19:24:09.877+0800: 433182.495: [Rescan (parallel) , 0.4336470 secs]2018-04-18T19:24:10.310+0800: 433182.929: [weak refs processing, 0.0042640 secs]2018-04-18T19:24:10.315+0800: 43
3182.933: [class unloading, 0.0233340 secs]2018-04-18T19:24:10.338+0800: 433182.957: [scrub symbol table, 0.0194050 secs]2018-04-18T19:24:10.358+0800: 433182.976: [scrub string table, 0.0028380 secs] [1 CMS-remark: 977603K(1310720K)] 1670308K(2018560K), 0.4921040 secs] 
[Times: user=11.89 sys=0.00, real=0.49 secs] 
2018-04-18T19:24:10.369+0800: 433182.988: [CMS-concurrent-sweep-start]
2018-04-18T19:24:11.194+0800: 433183.813: [CMS-concurrent-sweep: 0.472/0.825 secs] [Times: user=5.27 sys=0.00, real=0.83 secs] 
2018-04-18T19:24:11.194+0800: 433183.813: [CMS-concurrent-reset-start]
2018-04-18T19:24:11.203+0800: 433183.822: [CMS-concurrent-reset: 0.009/0.009 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 

这是其中一部分的日志,可以看到19:24:06之前几乎都是YoungGC,FullGC的发生次数应该不会太高,但是从19:24:06开始到19:40持续了上百次的FullGC,但是老年代的空间始终没有下来。

YoungGC日志

含义

我们取日志的第一行来认识一下各个字段的含义。

2018-04-18T19:21:50.142+0800: 433042.760: [GC2018-04-18T19:21:50.142+0800: 433042.760: [ParNew: 631407K->2100K(707840K), 0.0056650 secs] 959391K->330123K(2018560K), 0.0059200 secs] [Times: user=0.09 sys=0.00, real=0.00 secs] 

2018-04-18T19:21:50.142+0800: 433042.760(时间戳): [GC(Young GC)2018-04-18T19:21:50.142+0800: 433042.760(时间戳): [ParNew(使用ParNew作为年轻代的垃圾回收期): 631407K(年轻代垃圾回收前的大小)->2100K(年轻代垃圾回收以后的大小)(707840K)(年轻代的总大小), 0.0056650 secs(回收时间)] 959391K(堆区垃圾回收前的大小)->330123K(堆区垃圾回收后的大小)(2018560K)(堆区总大小), 0.0059200 secs(回收时间)] [Times: user=0.09(Young GC用户耗时) sys=0.00(Young GC系统耗时), real=0.00 secs(Young GC实际耗时)]

计算分析

以日志的最后两条YoungGC记录为例,最后一条记录显示年轻代回收了707840-78592=629248K,整个堆回收了1359527-894638=464889K,说明此次回收有629248-464889=164359K从年轻代转移到了老年代。
再看最后一次YoungGC前的情况,看倒数第二条记录,老年代的内存使用有730279-78592=651687K,回收后有894638-78592=816046K。
回收后老年代使用空间-回收前老年代使用空间=年轻代转向老年代空间(816046-651687=164359K)。

CMS

第一段日志后面那一段是CMS日志。

简介

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,适用于对响应速度有追求的系统,如BS网站,有助于提高用户体验度。使用的GC算法是标记清除,用来清理老年代空间。
总共分为⑤个步骤:
①初始标记(CMS-initial-mark)
②并发标记(CMS-concurrent-mark)
③重新标记(CMS-remark)
④并发清除(CMS-concurrent-sweep)
⑤并发重置状态等待下次CMS的触发(CMS-concurrent-reset)
其中,第一步初始标记和第三步重新标记会"Stop The World",也就是挂起应用程序。初始标记仅仅是标记GCRoots能直接关联的对象,速度快。所谓的并发标记、并发清除、并发重置中的并发是指回收线程也快和应用程序一起工作。重新标记就是重新标记并发标记阶段遗漏的对象(在并发标记阶段结束后对象状态的更新导致),这一步比初始标记时间长,但比并发标记时间短。

CMS
缺点

①对CPU资源敏感。在并发阶段,占用了CPU资源导致应用程序变慢,总吞吐量降低。CMS默认启动的回收线程是(CPU数量+3)/4,cpu的占用率随着核数增多而减小。
②无法收集浮动垃圾,可能出现“Concurrent Mode Failure”失败导致下一次FullGC的发生。因为CMS与应用程序并发执行,在清理的同时也有新的垃圾产生,而此次标记结束,只好下次GC,这部分垃圾称为浮动垃圾。
③CMS使用标记清除算法,该算法的缺点的是会产生大量的内存碎片,当程序需要分配一个大对象而无法满足空间要求时,就必须提前触发下一次FullGC。

涉及的VM参数

①-XX:+UseConcMarkSweepGC (使用CMS收集器)
②-XX:CMSInitiatingOccupancyFraction=N
CMS与应用程序并发执行,所以不能等老年代满了再清理,还是要预留一部分空间给应用线程使用。那么就需要设置一个阈值,当满足百分之多少了就进行FullGC,默认是68%,通过该参数可以设置阈值。
③-XX:+UseCMSCompactAtFullCollection,上面写了CMS会产生很多内存碎片,这个参数的作用就是当CMS收集器就是开启内存碎片合并,默认开始。合并的过程无法并发,停顿时间就会变长,会影响一定的性能。
④-XX:CMSFullGCsBeforeCompaction,用于设置执行多少次不压缩的FullGC,然后跟着来一次压缩的。
⑤-XX:ParallelCMSThreads=N,CMS默认启动的回收线程是(CPU数量+3)/4,如果不想默认就可以用它来设置。
⑥-XX:ParallelGCThreads= N,年轻代的并行收集线程数默认是(ncpus <= 8) ? ncpus : 3 + ((ncpus * 5) / 8),如果不想默认就可以用它来设置。

CMS日志解释
2018-04-18T19:24:06.946+0800: 433179.565: [GC [1 CMS-initial-mark: 816046K(1310720K)] 903563K(2018560K), 0.0997080 secs]
[Times: user=0.10 sys=0.00, real=0.10 secs] 
初始标记阶段,老年代空间使用量816046K(老年代空间总量1310720K),整个堆使用量903563K(整个堆总量2018560K),耗时0.0997080秒。
2018-04-18T19:24:07.046+0800: 433179.665: [CMS-concurrent-mark-start]
开始并发标记
2018-04-18T19:24:07.300+0800: 433179.918: [CMS-concurrent-mark: 0.254/0.254 secs] [Times: user=1.89 sys=0.00, real=0.26 secs] 
并发标记阶段结束,占用 0.254秒CPU时间, 0.254秒墙钟时间(也包含线程让出CPU给其他线程执行的时间),用户耗时1.89秒,系统耗时0秒,实际耗时0.26秒。
2018-04-18T19:24:07.300+0800: 433179.919: [CMS-concurrent-preclean-start]
开始预清理阶段,预清理也是一个并发执行的阶段。在本阶段,会查找前一阶段执行过程中,从新生代晋升或新分配或被更新的对象。
通过并发地重新扫描这些对象,预清理阶段可以减少下一个stop-the-world 重新标记阶段的工作量。
2018-04-18T19:24:07.314+0800: 433179.932: [CMS-concurrent-preclean: 0.013/0.013 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
预清理阶段耗时,0.013秒CPU时间,0.013秒墙钟时间。用户耗时0.03秒,系统耗时0秒,实际耗时0.01秒。
2018-04-18T19:24:07.314+0800: 433179.932: [CMS-concurrent-abortable-preclean-start]
开始可中断的预处理,该阶段发生的前提是,新生代Eden区的内存使用量大于参数CMSScheduleRemarkEdenSizeThreshold 默认是2M,如果新生代的对象太少,
就没有必要执行该阶段,直接执行重新标记阶段。
2018-04-18T19:24:09.876+0800: 433182.494: [CMS-concurrent-abortable-preclean: 2.006/2.562 secs] [Times: user=7.38 sys=0.23, real=2.56 secs] 
可中断的预处理耗时,2.006秒CPU时间,2.562秒墙钟时间。用户耗时7.38秒,系统耗时0.23秒,实际耗时2.56秒。
2018-04-18T19:24:09.877+0800: 433182.495: [GC[YG occupancy: 692704 K (707840 K)]
年轻代已使用692704K,年轻代中空间707840K。
2018-04-18T19:24:09.877+0800: 433182.495: [Rescan (parallel) ,0.4336470 secs]
重新标记,从根及被其引用对象开始,,parallel 表示多GC线程并行,重新扫描 CMS 堆中残留的更新过的对象耗时0.4336470秒。
2018-04-18T19:24:10.310+0800: 433182.929: [weak refs processing, 0.0042640 secs]
第一个子阶段,处理弱引用对象费时0.0042640秒,本阶段费时0.1897792 秒,。
2018-04-18T19:24:10.315+0800: 433182.933: [class unloading, 0.0233340 secs]
第二个子阶段(that is unloading the unused classes, with the duration and timestamp of the phase);
2018-04-18T19:24:10.338+0800: 433182.957: [scrub symbol table, 0.0194050 secs]
2018-04-18T19:24:10.358+0800: 433182.976: [scrub string table, 0.0028380 secs] 
最后一个子阶段(that is cleaning up symbol and string tables which hold class-level metadata and internalized string respectively)
[1 CMS-remark: 977603K(1310720K)] 1670308K(2018560K), 0.4921040 secs] [Times: user=11.89 sys=0.00, real=0.49 secs] 
重新标记后,老年代使用977603K(老年代总量1310720K),堆使用量1670308K(堆总量2018560K),耗时0.4921040秒。用户耗时11.89秒,系统耗时0秒,实际耗时0.49秒。
2018-04-18T19:24:10.369+0800: 433182.988: [CMS-concurrent-sweep-start]
开始并发清除
2018-04-18T19:24:11.194+0800: 433183.813: [CMS-concurrent-sweep: 0.472/0.825 secs] [Times: user=5.27 sys=0.00, real=0.83 secs]
并发清除耗时 0.472秒CPU时间,0.825秒墙钟时间。用户耗时5.27秒,系统耗时0秒,实际耗时0.83秒。
2018-04-18T19:24:11.194+0800: 433183.813: [CMS-concurrent-reset-start]
开始并发重置,本阶段重新初始化CMS内部数据结构,以备下一轮 GC 使用。
2018-04-18T19:24:11.203+0800: 433183.822: [CMS-concurrent-reset: 0.009/0.009 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
并发重置耗时 0.009秒CPU时间,0.009秒墙钟时间。用户耗时0.02秒,系统耗时0秒,实际耗时0.01秒。

总结

通过本篇基本就可以看懂YoungGC和CMS的日志了,介绍了CMS收集器的步骤、缺点、VM参数。其实GC这一块涉及到的内容很多,比如判断对象死活、四种GC算法、八种回收器等,也是第一次实战。
可是,扯了半天犊子还是没说是什么导致的FullGC频发。在我司框架师的帮助下定位到了一个下载Excel的接口,是因为操作者要下载一个百万条记录的excel,但是写excel使用的是HSSFWorkbook,当记录数过大时,就会有内存溢出的问题。最后换成了SXSSFWorkbook,在自动刷新的模式下,可以指定窗口中访问Row的数量,从而在内存中保持一定数量的Row。当达到这一数量时,在窗口中产生新的Row数据,并将低索引的数据从窗口中移动到磁盘中。

相关文章

  • 记一次Fullcut频发

    上周系统出现了FullGC频发的问题,应该是内存中又出现了大对象导致的,到底是分页查询的pageSize=1000...

  • 车祸频发

    2018年6月4日 星期一 晴 亲子日记86篇 2018年6月1日白天,在东海大桥桥面上先后发生了两起...

  • 感冒频发……

    今天看自己的工作微信群,发现好多同事感冒、发烧、咳嗽。 也不知是天气原因还是又有流行性感冒,不过不是新冠病毒,为此...

  • 创造无与伦比的独特美丽

    ——记参加李欣频老师的2018-2019量子跨年调频舞会心得 我第一次认识欣频老师是2017年年...

  • 勒索软件频发

    勒索软件攻击了马来西亚媒体巨头Media Prima Bhd,致使该公司员工无法访问电子邮件系统,黑客向该公司勒索...

  • 频发的地震

    昨天中国驻哈萨克斯坦大使馆发文提醒同胞:哈疫情形势日益严峻,日新增确诊病例数居高不下。请大家务必高度警惕,不麻痹、...

  • 频发与偶发

    今天和往常一样,睡到自然醒。吃了饭之后,老妈吃了早上该吃的药。总结笔记做好记录。 频繁发生的做到极致,时间利用率最...

  • 真假对比|贝德玛蓝水出假货了,有视频有真相!戳进来~

    Hello 魏蜀黍今天第一次发视频, 欢迎宝宝们捧场呀。 废话不多说,看!视!频!

  • 2019.02.08 周五

    过年期间状况频发&

  • 一次事故频发的外出活动

    今天是国家公祭日,我们学校参与了活动,而我们班级作为学校代表,参与活动,现场演唱歌曲。这是一次全区高级别的活动,区...

网友评论

      本文标题:记一次Fullcut频发

      本文链接:https://www.haomeiwen.com/subject/hxpzkftx.html