文章目录
一、概述
内存飙升:
- 内存泄漏(memory leak):是指程序在申请内存后,无法释放已申请的内存空间,堆积导致内存溢出。
- 内存溢出 (out of memory):是指程序申请内存时,没有足够的内存供申请者使用,导致数据无法正常存储到内存中。
CPU飙升:
CPU飙升会导致系统响应缓慢甚至服务不可用。top命令查看cpu,cpu飙升导致服务假死。
内存飙升常见原因:
-
其他应用(Redis、Kafka)占用总内存;
解决:top命令排除。
-
启动参数内存值设定的过小;
解决:-Xms,-Xmx。
-
内存中加载的数据量过于庞大(数据缓存、PDF字体缓存、文件传输);
解决:代码走查,观察内存波动。
-
List、MAP等集合中对对象的引用,使用完后未清空,使得JVM不能回收;
解决:代码走查,观察内存波动。
-
代码中存在死循环或循环产生过多重复的对象实体;
解决:代码走查。
CPU飙升常见原因:
-
死循环、递归
消耗CPU。
-
正则
消耗CPU。
-
线程数
线程数增多,通常会伴随内存飙升,但是线程内本身无复杂业务,会呈现,只有CPU飙升,但内存增高不明显。
-
死锁
死锁会导致核心线程数无响应,间接导致线程数飙升。
二、分析思路
内存飙升:
1.排查进程占用内存
- 使用ps命令查看内存占用情况。
2.分析内存使用情况
-
使用jstat工具查看Full GC情况,分析full gc次数是否频繁,确认应用本身是否有问题。
-
使用jmap查看当前应用进程使用内存,分析是否存在内存飙升等问题。
3.排查线程阻塞
- 使用netstat检查应用的连接数,排除线程阻塞原因。
CPU飙升:
top定位消耗CPU最高进程,jstack找到具体代码位置。
三、实际操作
内存飙升:
1.排查进程占用内存
方法一:
# 第五列为虚拟内存占用情况,第六列为实际内存占用情况,默认单位kb。
ps -aux | grep MyApp.jar
# 实际内存以M为单位
ps -aux | grep MyApp.jar | awk '{sum=$6/1024} {print $0 " " $1 " " sum " MB"}'
方法二:
# 通过ps查找进程id
ps -ef | grep MyApp.jar
# 通过top -p命令查看内存占用
top -p <pid>
2.分析内存使用情况
Java Heap Dump 是特定时刻 JVM 内存中所有对象的快照。它们对于解决内存泄漏问题和分析 Java 应用程序中的内存使用情况非常有用。
Java Heap Dump 通常以二进制格式的 hprof 文件存储。我们可以使用 jhat 或、JVisualVM 、MAT之类的工具打开和分析这些文件。
> Dump快照
1.生成dump快照
-
自动生成dump快照信息:
在OOM的时候会自动生成快照信息。
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/tmp/heapdump.hprof -jar MyApp.jar
-
手动生成dump快照信息:
如果启动脚本没有配置dump日志,不要直接停掉应用,手动生成dump日志。JVM 生成 Heap Dump 的时候,虚拟机是暂停一切服务的。如果是线上系统执行 Heap Dump 时需要注意。
# 0.查看PID ps -aux | grep MyApp.jar jps ################################### # 1.1生成dump日志 jmap -dump:format=b,file=/tmp/jmap_heapdump.hprof <pid> # 1.2生成dump日志(live只保留存活的对象),如果运行时间长,防止heap过大 jmap -dump:live,format=b,file=/tmp/heapdump.hprof <pid> ################################### # jcmd是java 8引入的集大成的诊断工具。 # 使用jcmd生成dump日志 jcmd <pid> GC.heap_dump /tmp/jcmd_heapdump.hprof
2.解析dump日志
-
MAT(免费,解析Dump日志,得出可疑分析报告)
Memory Analyzer Tool,下载失败,多点几次就行了。Memory Analyzer1.13.0需要jdk11。
官网地址:https://www.eclipse.org/mat/
-
JProfiler(付费,可试用10天,连接应用直观分析JVM各种变化)
官网:https://www.ej-technologies.com/download/jprofiler/files
富贵码:# 12.0版本 L-J12-STALKER#5846458-y8bdm6q8gtr7b#228a L-J12-STALKER#8338547-qywh5933xu2r3#a4a4
-
visualvm(免费)
官网:https://visualvm.github.io/
github:https://github.com/oracle/visualvm
> GC日志
-
启动参数配置生成gc日志:
java -Xloggc:gc.log -jar MyApp.jar
3.排查线程阻塞
检查连接数
netstat -nap | grep <pid>
CPU飙升:
定位消耗CPU最高代码
# 1.找到cpu最高的进程的id
top -c
# 查询java进程前五并进行排序
ps aux|grep java|grep -v grep|head -5|awk '{print $3,$1,$2}'|sort -rn
# 2.打印这个进程的所有线程的运行堆栈
jstack -l <pid> > /tmp/jstack.log
# 3.当前进程所有线程消耗情况
top -H -p <pid>
ps -mp <pid> -o THREAD,tid,time
# 4.找到CPU负载最高的线程, 把线程ID转换成16进制,(10进制转16进制,printf "%x\n" tid)
printf "%x\n" <tid>
# 5.搜索16进制显示的线程ID,定位到具体代码
vim jstack.log
# -A100是日志行数
# jstack <pid>|grep 16进制线程号 -A100
四、GC调优
-
jmap:查看内存信息
# 查看新生代老年代 jmap -heap pid # 生成dump文件 jmap -dump:live,format=b,file=/tmp/heapdump.hprof <pid>
-
Jstack:进程、线程、堆栈
# 打印这个进程的所有线程的运行堆栈 jstack -l <pid> # 输出heap的直方图,包括类名,对象数量,对象占用大小 jmap -histo <pid> # 输出堆内存设置和使用情况(JDK11使用jhsdb jmap --heap --pid pid) jmap -heap <pid>
-
Jinfo:查看jvm的参数
Jinfo <pid>
-
Jstat:堆内存各部分的使用量,以及加载类的数量
# 输出gc信息,包括gc次数和时间,内存使用状况 jstat -gc <pid> jstat -gcutil <pid>
-
Jcmd:从JDK 7开始提供,jcmd拥有jmap的大部分功能,官方推荐使用jcmd命令替代jmap命令。
# 列出所有的JVM进程 jcmd -l # 针对指定的进程,列出支持的所有命令 jcmd <pid> help