原创:扣钉日记(微信公众号ID:codelogs),欢送分享,非公众号转载保留此申明。
简介
在之前的OOM问题复盘之后,本周,又一Java服务呈现了内存问题,这次问题不重大,只会触发堆内存占用高报警,没有触发OOM,但好在之前的复盘中总结了dump脚本,会在堆占用高时主动执行jstack与jmap,使得咱们胜利保留了问题现场。
查看堆占用散布
发现有heapdump文件后,我立马拷贝到本机,并应用MAT剖析,如下:
很显然,如同是什么接口调配了十分大的String对象,一个String对象约200MB,那它是哪调配的呢?
查找大对象调配线程
这个调配行为必定是某个线程做的,而线程是最常见的GC Root,因而只有查找对象的GC Root即可,如下:
找到了大对象对应的调配线程是http-nio-8088-exec-6,如下:
查看线程栈
如何查看这个线程在干什么呢?在MAT中摸索了一会,没找到相干内容,回想起咱们的dump脚本中记录了jstack,关上看看,如下:
能够发现,这个线程正在做json序列化,但我认真找了好一会,也没有找到相干接口的Controller,这是因为线程曾经执行完了Controller外面的逻辑,之后返回接口响应数据时调配的大对象。
可是,线程栈中没有业务代码,就没法定位是哪个接口有问题了。。。
查看accesslog日志
思考到调配大对象的接口必定会很慢,于是我转向查看tomcat的accesslog日志,如下:
终于,找到了问题接口,这个接口是用来查问商品数据的,当输出3时会查问出所有3结尾的商品,而这有20w+数据,解决问题很简略,加个limit完事。
排查过程复盘
然而,我始终有个习惯,就是解决一个问题后,我会反思一下问题解决过程中有多少运气成分。
如果你常常浏览排查问题类的技术文章,就会发现不少文章,两头忽然有一步定位到了问题根因,可能是忽然发现了一个线索,或是硬看代码看进去的,或是猜想某处有问题,我感觉这种排查过程都有不少运气成分,我心愿问题是通过多年实践根底的积攒和对诊断工具的纯熟应用,而有章法的一步步查出来的。
而下面通过accesslog可能定位到问题,有肯定的运气成分,因为本次内存问题不极其,如果此接口申请量大,那就会霎时触发屡次FGC,进而会影响其它接口也变慢,进而无奈分辨出哪个是导致问题的接口!
我想,从实践上来说,Java堆文件外面,应该有线程栈以及线程栈上的参数,因为线程是对象,参数也是对象,它们理当都在堆里,于是我找了个闲暇工夫,又摸索起MAT这个工具了。
MAT查看线程栈
摸索了一会,我就发现有这样一个按钮,能够查看线程信息,如下:
找到后面说的线程http-nio-8088-exec-6,开展后,就能够发现线程栈以及栈上的参数,如下:
这就找到了申请的Request参数对象,再将Request对象屡次开展后,就能够找到接口url信息,如下:
嗯,这样剖析heapdump文件真tm的高效啊
MAT下载地址:https://www.eclipse.org/mat/downloads.php
VisualVM查看线程栈
思考到不少同学习惯用VisualVM剖析heapdump,这里也放一下VisualVM的应用办法。
首先,加载heapdump文件,如下:
而后抉择相应对象,右键抉择Select in Threads,如下:
定位到线程栈后,找到要查看的Request对象,点击进入,如下:
同样,开展Request对象后,可找到url信息,如下:
VisualVM下载地址:https://visualvm.github.io/download.html
总结
尽管我也用MAT很屡次了,但每次问题都太简略,以至于没有深刻应用过MAT,导致到当初才晓得有如此便捷的剖析门路。
如果你对咱们的主动dump脚本感兴趣,可看看我之前写的这两篇文章。
一次线上OOM问题的集体复盘
jmap执行失败了,怎么获取heapdump?