定位 Java 程序内存使用过高或者内存泄漏的问题跟 CPU 也类似,一般可以分为以下 3 个步骤:
- 定位进程
- 定位线程
- 定位具体方法(代码部分)
一、定位进程
通过 top -c
(然后按Shift+M
按内存排序),或者 htop
等工具定位到具体的高内存进程。假设定位到的进程 ID 为 14279。
二、定位线程
2.1 通过 top 查看线程
top -H -p 14279
(然后按 Shift+M
按内存排序)定位占内存的线程:
%Cpu(s): 0.5 us, 0.7 sy, 0.0 ni, 98.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 8168236 total, 231696 free, 3660496 used, 4276044 buff/cache
KiB Swap: 969964 total, 969964 free, 0 used. 4197860 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
14293 weiping 20 0 4508772 97036 18112 S 10 12 152:35.42 java
14279 weiping 20 0 4508772 97036 18112 S 5.0 1.2 0:00.00 java
14282 weiping 20 0 4508772 97036 18112 S 0.0 1.2 0:00.37 java
2.2 通过 ps 统计下当前进程的线程数
ps p 14279 -L -o pcpu,pmem,pid,tid,time,tname,cmd |wc -l
2.3 初步判断
通过以上二步确认是否线程开多了,还是单个线程内存占用过多导致。
- 如果是线程过多,那么就要去排查具体原因。是服务器线程,还是业务代码中的多线程导致?
- 如果是单线程内存占用,那么就要 dump 快照或者本地尝试模拟重现。
2.4 查看线程信息
通过 thread tid
直接查看指定线程的堆栈信息:
[arthas@42436]$ thread 91
"MQ-AsyncTraceDispatcher-Thread-ce0ebd2a-5807-4053-ae3c-7472fe4b5aef" Id=91 TIMED_WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@774405a1
at sun.misc.Unsafe.park(Native Method)
-- waiting on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@774405a1
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.ArrayBlockingQueue.poll(ArrayBlockingQueue.java:418)
at org.apache.rocketmq.client.trace.AsyncTraceDispatcher$AsyncRunnable.run(AsyncTraceDispatcher.java:238)
at java.lang.Thread.run(Thread.java:748)
Affect(row-cnt:0) cost in 2 ms.
更多官方详细文档:Arthas 命令文档——thread
三、定位具体方法
3.1 通过 jmap dump 内存快照
如果是线上环境,注意 dump 之前必须先将流量切走,否则大内存 dump 是直接卡死服务。```
# dump 当前快照
jmap -dump:live,format=b,file=dump.hprof <pid>
# 触发 full gc,然后再 dump 一次
jmap -dump:live,format=b,file=dump_gc.hprof <pid>
```
dump:live 的作用是会触发 Full GC,然后再 dump 数据,用作 gc 前后的数据做对比。
3.2 使用 MAT 分析
如果快照文件不大,可以下载到本地,然后通过 MAT 分析。
3.3 上传到 fastthread.io 分析
如果快照文件不大,也可以上传到 https://fastthread.io/ 分析。
3.4 通过 jhat 分析
如果快照文件很大,可以在服务器上直接分析:
faceless@ttg12:~/tmp$ jhat dump.hprof
Reading from dump.hprof...
Dump file created Mon Jun 22 14:33:00 CST 2020
Snapshot read, resolving...
Resolving 36246 objects...
Chasing references, expect 7 dots.......
Eliminating duplicate references.......
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
分析完成后,访问 http://ttg12:7000,如下图:
点进入看实例数(图来自网络,这里只是示意):
找到数量大的业务类,比如上图中是Packet
。然后一路点进去,跟踪引用路径,看到底是哪个类引用的。
3.5 通过 Arthas 分析
TO BE FINISHED——也可以通过服务器上的 Arthas 分析。