原文链接:
https://blog.csdn.net/sinat_40770656/article/details/122203566
首先,我们需要了解有哪些常用的性能监控和jvm调优的工具:
传送门:jvm参数详解
比较常见的一些参数配置:
堆配置:
GC收集器配置:
······
JVM调优是一件很严肃的事情,前期需要严密的分析和监控。实际上,JVM调优是不得已而为之,有那功夫,好好把烂代码重构一下不比瞎调JVM参数强么?
面试官非要问怎么办?可以从处理问题的角度来回答(对应图中事后),这是一个中规中矩的案例:电商公司的运营后台系统,偶发性的引发OOM异常,堆内存溢出。
1、因为是偶发性的,所以第一次简单的认为就是堆内存不足导致,单方面的加大了堆内存从4G调整到8G -Xms8g。
2、但是问题依然没有解决,只能从堆内存信息下手,通过开启了-XX:+HeapDumpOnOutOfMemoryError参数 获得堆内存的dump文件。
3、用JProfiler 对 堆dump文件进行分析,通过JProfiler查看到占用内存最大的对象是String对象,本来想跟踪着String对象找到其引用的地方,但dump文件太大,跟踪进去的时候总是卡死,而String对象占用比较多也比较正常,最开始也没有认定就是这里的问题,于是就从线程信息里面找突破点。
4、通过线程进行分析,先找到了几个正在运行的业务线程,然后逐一跟进业务线程看了下代码,有个方法引起了我的注意,导出订单信息。
5、因为订单信息导出这个方法可能会有几万的数据量,首先要从数据库里面查询出来订单信息,然后把订单信息生成excel,这个过程会产生大量的String对象。
6、为了验证自己的猜想,于是准备登录后台去测试下,结果在测试的过程中发现导出订单的按钮前端居然没有做点击后按钮置灰交互事件,后端也没有做防止重复提交,因为导出订单数据本来就非常慢,使用的人员可能发现点击后很久后页面都没反应,然后就一直点,结果就大量的请求进入到后台,堆内存产生了大量的订单对象和EXCEL对象,而且方法执行非常慢,导致这一段时间内这些对象都无法被回收,所以最终导致内存溢出。
7、知道了问题就容易解决了,最终没有调整任何JVM参数,只是做了两个处理:
这样双管齐下,保证导出的请求不会一直打到服务端,问题解决!
分析:CPU高一定是某个程序长期占用CPU资源
1、 所以,先找出那个进程占用CPU高。
2、然后,根据找到对应进程里那个线程占用CPU高。
3、找到对应进程后,再打印出对应线程的堆栈信息。
4、最后根据线程的堆栈信息定位到具体业务方法,从代码逻辑中找到问题所在。
查看是否有线程长时间的waitting或blocked,如果线程长期出入waitting状态下,关注waitting on xxxxx,说明线程在等待这把锁,然后根据锁的地址找到持有锁的线程。
分析: 内存飙高如果是发生在java进程上,一般是因为创建了大对象所导致,持续飙高说明垃圾回收跟不上对象创建的速度,或者内存泄漏导致对象无法回收。
1、先观察垃圾回收的情况
如果每次GC次数频繁,而且每次回收的内存空间也正常,那是说明因为对象创建速度快导致内存一直占用很高;如果每次回收内存非常少,那么很可能是因为内存泄漏导致内存一直无法被回收。
2、导出堆内存文件快照
3、使用visualVM对dump文件进行离线解析,找到占用内存高的对象,再找到创建该对象的业务代码位置,从代码和业务场景中定位具体具体问题。
通常情况下,可能是年轻代空间较小,Eden区很快被填满,导致频繁的minor gc;因此,可以调大年轻代空间(-Xmx),来降低minor gc次数。
思路:
1、清楚从程序角度,有哪些原因导致FGC。
2、清楚排查问题时使用哪些工具
# 查看堆内存各区域的使用率以及GC情况
jstat -gcutil -h20 pid 1000
# 查看堆内存中存活对象,并按空间排序
jmap -histo pid | head -n20
# dump堆内存文件
jmap -dump:format=b,file=heap pid
3、排查指南
内存泄漏是内在病源,外在症状可能为:
严重的内存泄漏往往伴随着频繁的Full GC,所以分析排查内存泄漏问题首先还得从查看 Full GC 入手。主要有以下操作步骤:
使用 jps 查看运行的JAVA进程ID;
使用 top -p pid 查看进程使用CPU和MEM的情况;
使用 top -Hp pid 查看进程下所有线程占用CPU和MEM的情况;
将线程 ID 转换为 16 进制:printf “%x\n” pid ,输出的值就是线程栈信息中的 nid。
例如:printf “%x\n” 29471,换行输出 731f。
抓取线程栈:jstack 29452 > 29452.txt,可以多抓几次做个对比。
在线程栈信息中找到对应线程号的 16 进制值,如下是 731f 线程的信息。线程栈分析可使用 Visualvm 插件 TDA。
使用 jstat -gcutil [pid] 5000 10 每隔 5 秒输出 GC 信息,输出 10 次,查看 YGC 和 Full GC 次数。通常会出现 YGC 不增加或增加缓慢,而 Full GC 增加很快。
或使用 ** jstat -gccause [pid] 5000 ** ,同样是输出 GC 摘要信息。
或使用 jmap -heap [pid] 查看堆的摘要信息,关注老年代内存使用是否达到阀值,若达到阀值就会执行 Full GC。
如果发现 Full GC 次数太多,就很大概率存在内存泄漏了
使用 **jmap -histo:live [pid] ** 输出每个类的对象数量,内存大小(字节单位)及全限定类名。
生成 dump 文件,借助工具分析哪 个对象非常多,基本就能定位到问题在那了
使用 jmap 生成 dump 文件:
# jmap -dump:live,format=b,file=29471.dump 29471
dump 文件分析:
可以使用 jhat 命令分析:jhat -port 8000 29471.dump,浏览器访问 jhat 服务,端口是 8000。
通常使用图形化工具分析,如 JDK 自带的 jvisualvm,从菜单 > 文件 > 装入 dump 文件。
或使用第三方式具分析的,如 JProfiler 也是个图形化工具,GCViewer 工具。Eclipse 或以使用 MAT 工具查看。或使用在线分析平台 GCEasy。
注意:如果 dump 文件较大的话,分析会占比较大的内存。
在 dump 文析结果中查找存在大量的对象,再查对其的引用。基本上就可以定位到代码层的逻辑了。
内存泄漏和内存溢出二者关系非常密切,内存溢出可能会有很多原因导致,内存泄漏最可能的罪魁祸首之一。
排查过程和排查内存泄漏过程类似。