共计 1622 个字符,预计需要花费 5 分钟才能阅读完成。
原创:扣钉日记(微信公众号 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?