关于gc:线上FullGC问题排查实践手把手教你排查线上问题-京东云技术团队

作者:京东科技 韩国凯

一、问题发现与排查

1.1 找到问题起因

问题起因是咱们收到了jdos的容器CPU告警,CPU使用率曾经达到104%

察看该机器日志发现,此时有很多线程在执行跑批工作。失常来说,跑批工作是低CPU高内存型,所以此时思考是FullGC引起的大量CPU占用(之前有相似状况,告知用户后重启利用后解决问题)。

通过泰山查看该机器内存应用状况:

能够看到CPU的确使用率偏高,然而内存使用率并不高,只有62%,属于失常范畴内。

到这里其实就有点蛊惑了,按情理来说此时内存应该曾经打满才对。

前面依据其余指标,例如流量的忽然进入也狐疑过是jsf接口被忽然大量调用导致的cpu占满,所以内存使用率不高,不过前面都缓缓排除了。其实在这里就有点束手无策了,景象与猜想不符,只有CPU增长而没有内存增长,那么什么起因会导致单方面CPU增长?而后又朝这个方向排查了半天也都被否定了。

前面忽然意识到,会不会是监控有“问题”?

换句话说应该是咱们看到的监控有问题,这里的监控是机器的监控,而不是JVM的监控!

JVM的应用的CPU是在机器上能体现进去的,而JVM的堆内存高额应用之后在机器上体现的并不是很显著。

遂去sgm查看对应节点的jvm相干状况:

能够看到咱们的堆内存老年代的确有过被打满而后又清理后的状况,查看此时的CPU应用状况也能够与GC工夫对应上。

那么此时能够确定,是Full GC引起的问题。

1.2 找到FULL GC的起因

咱们首先dump出了gc前后的堆内存快照,

而后应用JPofiler进行内存剖析。(JProfiler是一款堆内存剖析工具,能够间接连接线上jvm实时查看相干信息,也能够剖析dump进去的堆内存快照,对某一时刻的堆内存状况进行剖析)

首先将咱们dump进去的文件解压,批改后缀名.bin,而后关上即可。(咱们应用行云上自带的dump小工具,也能够本人去机器上通过命令手工dump文件)

首先抉择Biggest Objects,查看过后堆内存中最大的几个对象。

从图中能够看出,四个List对象就占据了近900MB的内存,而咱们刚刚看到堆内存最大也只有1.3GB,因而再加上其余的对象,很容易就会把老年代占满引发full gc的问题。

抉择其中一个最大的对象作为咱们要查看的对象

这个时候咱们曾经能够定位到对应的大内存对象对应的地位:

其实至此咱们曾经可能大略定位出问题所在,如果还是不确定的话,能够查看具体的对象信息,办法如下:

能够看到咱们的大List对象,其实外部是很多个Map对象,而每个Map对象中又有很多键值对。

在这里也能够看到Map中的相干属性信息。

也能够在以下界面间接看到相干信息:

而后一路点上来就能够看到对应的属性。

至此,咱们实践上曾经找到了大对象在代码中的地位。

二、问题解决

2.1 找到大对象在代码中的地位与问题的根本原因

首先咱们根据上述过程找到对应地位与逻辑

咱们的我的项目中大略逻辑是这样的:

  1. 首先会解析用户上传的Excel样本,并将其加载到内存中作为一个List变量,即咱们上述看到的变量。一个20w的样本,此时字段数量有a个,大略占用空间100mb左右。
  2. 而后遍历循环用户样本,依据用户样本中的数据,再减少一些额定的申请数据,依据此数据申请相干后果。此时字段数量有a+n个,占用空间曾经在200mb左右。
  3. 循环实现后将此200mb的数据存入缓存。
  4. 开始生成excel,将200mb数据从缓存中取出,并依据之前记录的a个字段,取出初始的样本字段填充至excel。

用流程图示意为:

联合一些具体排查问题的图片:

其中一个景象是每次gc后的最小内存正在逐渐变大,对应上述步骤中第二步,内存正在逐渐收缩。

论断

将用户上传的excel样本加载到内存中,并将其作为一个List<Map<String, String>>的构造存储起来,首先一个20mb的excel文件以此形式存储会收缩占用120mb左右堆内存,此步骤会大量占用堆内存,并且因为工作逻辑起因,该大对象内存会在jvm中存在长达4-12小时之久,导致一但工作过多,jvm堆内存很容易被打满。

这里列举了为什么应用HashMap会导致内存收缩,其次要起因是存储空间效率比拟低:

一个Long对象占内存计算:在HashMap<Long,Long>构造中,只有Key和Value所寄存的两个长整型数据是无效数据,共16字节(2×8字节)。这两个长整型数据包装成java.lang.Long对象之后,就别离具备8字节的MarkWord、8字节的Klass指针,再加8字节存储数据的long值(一个包装对象占24字节)。

而后这2个Long对象组成Map.Entry之后,又多了16字节的对象头(8字节MarkWord+8字节Klass指针=16字节),而后一个8字节的next字段和4字节的int型的hash字段(8字节next指针+4字节hash字段+4字节填充=16字节),为了对齐,还必须增加4字节的空白填充,最初还有HashMap中对这个Entry的8字节的援用,这样减少两个长整型数字,理论消耗的内存为(Long(24byte)×2)+Entry(32byte)+HashMapRef(8byte)=88byte,空间效率为无效数据除以全副内存空间,即16字节/88字节=18%。

——《深刻了解Java虚拟机》5.2.6

以下是刚上传的excel中dump出的堆内存对象,其占用的内存达到了128mb,而上传的excel理论只有17.11mb。

空间效率17.1mb/128mb≈13.4%

2.2 如何解决此问题

暂且不探讨上述流程是否正当,解决办法个别能够分为两类,一类是治标,即不把该对象放入jvm内存中,转而存入缓存中,不在内存中则大对象问题天然迎刃而解。另一类是治本,即放大该大内存对象,在日常应用场景下使其个别不会触发频繁的full gc问题。

两种形式各有优劣:

2.2.1 激进医治:不把他存入内存

解决逻辑也很简略,例如在加载数据时,将其依照样本加载数据一条一条存入redis缓存,而后咱们只须要晓得样本中有多少的数量,依照数量的先后顺序从缓存中取出数据,即可解决该问题。

长处:能够从根本上解决此问题,当前基本上不会存在该问题,数据量再大只须要增加相应的redis资源即可。

毛病:首先会减少许多redis缓存空间耗费,其次从显示思考对于咱们我的项目来说,此处代码古老且艰涩难懂,改变须要较大工作量与回归测试。

2.2.2 激进医治:缩减其数据量

剖析2.1的上述流程,首先第三步是齐全没必要的,先存入缓存再取出,额定占用缓存空间。(猜想系历史问题,此处不再深究)。

其次是在第二步中,多进去的字段n,在申请完结后该字段就曾经无用了,因而能够思考在申请完结后删除无用字段。

此时也有两种解决方案,一种是只删除无用字段缩减其map大小,而后将其作为参数传递给生成excel应用;另一种形式是申请实现间接删除该map,而后在生成excel时再从新读取用户上传的excel样本。

长处:改变较小,不须要太简单的回归测试

毛病:在极其大数据量状况下,仍有可能呈现full gc的状况

具体实现形式就不开展了。

其中一种实现形式

//获取有用的字段
String[] colEnNames = (String[]) colNameMap.get(Constant.BATCH_COL_EN_NAMES);
List<String> colList = Arrays.asList(colEnNames);
//去除无用的字段
param.keySet().removeIf(key -> !colList.contains(key));

三、拓展思考

首先本文中监控图是在复现过后场景时人为制作的gc常见。

在cpu使用率图中,大家能够察看到cpu使用率上升时间的确跟gc的工夫相吻合,然而并没有呈现过后场景中的104%的CPU使用率

其实间接起因比较简单,就是因为零碎尽管呈现了full gc,然而并没有频繁呈现。

小范畴低频率的full gc不太会引起零碎的cpu飙升,这也是咱们所看到的景象。

那么过后的场景是什么起因呢?

咱们上文提到过,咱们在堆内存中的大对象是会随着工作的进行逐渐收缩的,那么当咱们的工作足够多,工夫足够长,就有可能导致每次full gc后可用空间变得越来越小,当可用空间小到肯定水平之后就,每次full gc实现之后发现空间还是不够应用,就会触发下一次的gc,从而导致最终后果的频繁产生gc,引起cpu频率的飙升不下。

四、问题排查总结

  • 当咱们遇到线上cpu使用率过高的状况时,能够先查看是否是full gc引起的问题,留神要看的是jvm的监控,或者应用jstat相干命令查看。不要被机器内存监控所误导。
  • 如果确定是gc引起的问题,能够通过JProfiler直连线上jvm或者应用dump保留堆快照后离线剖析。
  • 首先能够找到最大的对象,个别状况下是大对象引起的full gc。还有一种状况是,不像这么显著是四个大对象,也可能是比拟平衡的十几个50mb的对象,具体情况还须要具体分析。
  • 通过上述工具找到确定有问题的对象后找到其堆栈对应的代码地位,通过代码剖析找到问题的具体起因,通过其余景象推演猜想是否正确,进而找到问题的真正起因。
  • 依据问题的起因解决此问题。

当然,上述只是不算很简单的排查状况,不同的零碎必定有不同的内存状况,咱们该当具体问题具体分析,而从此次问题中能够学到的就是如果排查解决问题的思路。

【腾讯云】轻量 2核2G4M,首年65元

阿里云限时活动-云数据库 RDS MySQL  1核2G配置 1.88/月 速抢

本文由乐趣区整理发布,转载请注明出处,谢谢。

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据