结论
先说结论,后端服务消费到了kafka的超大消息,导致解析Json消息时OOM内存溢出了。
背景
有天,快下班时用户反馈说系统不可用了,加班排查了一晚上给解决了。
确定问题
1查CPU
登服务器,top命令监控当时CPU情况,发现有个java进程CPU飙升到100%了。
2定位系统
使用 ps -ef | grep java 命令,查这台服务器运行的所有java进程PID,发现cpu飘高的进程正好是我们XX系统出的问题,导致系统不可用。
3定位问题
再结合系统logback的error.log异常日志文件,发现OutOfMemoryError: Java heap spac,更证实了OOM堆内存溢出,导致的问题。 此外,还发现个问题:KafkaMessageListenerContainer异常了,难道系统用的Kafka接收消息出了问题?在这先埋下伏笔
分析问题
1下载dump文件
dump文件定义: dump文件是java虚拟机内存在某一时间点的快照文件,一般是.hprof的扩展名文件。 结合系统设置的生产环境JVM参数,可见:-Xmx1024m -Xms1024m -Xss512k Xms:堆内存的初始大小,默认为物理内存的1/64 Xms:堆内存的最大大小,默认为物理内存的1/4 Xss:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。 -XX:+HeapDumpOnOutOfMemoryError 表示当JVM发生OOM时,自动生成dump文件,dump文件在:KaTeX parse error: Expected group after '_' at position 23: …G_PATH}java_pid_?{APPNAME}%t%p.hprof 路径下。 找到该dump文件路径,用sz命令下载该dump文件
2了解MAT工具
MAT(Memory Analyzer Tool)定义:一种内存分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。 在eclipse上有mat的插件,或者苹果电脑有也有单独下载的mac版本mat,均可百度自行安装。
3导入mat分析:
按图下图所示,导入文件 导入完成,如下图所示 点击深蓝色的饼图部分,我们解读到:一共935.5MB的内存空间,KafkaMessageListenerContainer这个线程线程竟然占据了822.2M,其中Shallow size占了120B,Retained Size占了822.2M。 Shallow Size:对象自身占用的内存大小,不包括它引用的对象。 Retained Size:可理解为当前对象被GC后,从Heap上总共能释放掉的内存。 我们知道,频繁的内存泄露会导致内存溢出,大部分因为没及时垃圾回收清除对象释放内存空间,接着点击Leak Suspects(内存泄露建议)按钮如下: 已经再次提示了:KafkaListenerEndpointContainer 这个线程,本地变量,占据了862,169,408 (87.89%) bytes 查看堆栈信息,点击see stackstrace,如下图,定位到了具体哪行的问题代码 结合代码得知,就是这第109行的代码,做json解析时出问题导致, 我这代码大概逻辑是:kafka接收到json格式的消息后,再执行这行代码做json解析。 我们再返回上一步,点击Details看详情: 进入发现了,熟悉的消息格式,右键把该消息复制为文件后,发现这个消息居然占:169MB! 该消息平常就几十到几百KB。
4 结论分析
至此,我们得出结论:某天线上突然有个超大(169MB)的消息要消费,解析json时导致的。
改进措施
把上述有问题的代码,前面加上过滤,只处理指定阈值大小内(消息的字符串长度在2097152以内)的消息,才做json解析,否则业务上视为异常消息,不处理。
|