原创:扣钉日记(微信大众号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下载地址:www.eclipse.org/mat/downloa…
VisualVM检查线程栈
考虑到不少同学习惯用VisualVM剖析heapdump,这里也放一下VisualVM的运用方法。
首先,加载heapdump文件,如下:
然后挑选相应目标,右键挑选Select in Threads,如下:
定位到线程栈后,找到要检查的Request目标,点击进入,如下:
同样,展开Request目标后,可找到url信息,如下:
VisualVM下载地址:visualvm.github.io/download.ht…
总结
虽然我也用MAT很屡次了,但每次问题都太简单,以至于没有深入运用过MAT,导致到现在才知道有如此快捷的剖析途径。
假如你对咱们的自动dump脚本感兴趣,可看看我之前写的这两篇文章。
一次线上OOM问题的个人复盘
jmap执行失利了,怎样获取heapdump?