[总结]如何快速定位JVM中消耗CPU最多的线程?
在日常 Java 的开发中,性能调优肯定是很多人不能绕开的一个环节。而其中最简单,也是最基础的一个问题就是如何定位消耗 CPU 最多的线程。这篇文章中你假笨以一个简单的 Test 例子为蓝本,给各位总结了分析这类问题的常用『套路』。
具体如下。
这个例子里新创建了 11 个线程,其中 10 个线程没干什么事,主要是 sleep,另外有一个线程在循环里一直跑着,可以想象这个线程是这个进程里最耗 CPU 的线程了,那怎么把这个线程给抓出来呢?
1. windows版
1-1-1-1. 查消耗cpu最高的进程PID
手边没有windows server机器,我以win 10为例,截图给大家看一下,先调出PID显示项!
然后发现进程PID如下图所示,为10856
接下来呢?
1-1-1-2. 根据PID查出消耗cpu最高的线程号
这里用到微软的工具Process Explorer v16.22,地址如下
https://docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer
如图所示
发现最耗cpu的线程的TId为6616
这是十进制的数据,转成十六进制为19d8
1-1-1-3. 根据线程号查出对应的java线程,进行处理
执行命令,导出进程快照
1 |
jstack -l 10856 > c:/10856.stack |
打开文件 c:/10856.stack,搜索19d8,如下图所示
根据文件就可以看出,我们的TestFor.java
文件第七行一直在跑,至此定位到问题
2. Linux版
执行top -c
,显示进程运行信息列表。按下P, 进程按照cpu使用率排序
1. 可以使用 jps -v 这样的方式列出所有的 Java 进程
2. 当然如果知道关键字的话直接使用 ps aux|grep java 也是可以的。
如下图所示,PID为3033的进程耗费cpu最高
首先我们可以通过top -Hp <pid>
来看这个进程里所有线程的 CPU 消耗情况,得到类似下面的数据。
拿到这个结果之后,我们可以看到 cpu 最高的线程是 pid 为 18250 的线程,占了 99.8%:
接着我们可以通过 jstack的输出来看各个线程栈:
1 |
jstack -l 3033 > ./3033.stack |
如果遇到错误
1 |
Unable to open socket file: target process not responding or HotSpot VM not loaded |
The -F option can be used when the target process is not responding
可以加参数 -F:
1 |
jstack -F 18207 |
执行命令,导出进程快照
1 |
jstack -l 18207 > ./18207.stack |
上面的线程栈我们注意到 nid 的值其实就是线程 ID,它是十六进制的,我们将消耗 CPU 最高的线程18250
,转成十六进制0X47A
,然后从上面的线程栈里找到nid=0X47A
的线程,其栈为:
然后执行,grep命令,看线程0xbda
做了什么
1 |
cat 18207.stack |grep '47a' -C 8 |
1 2 |
"Busiest Thread" #28 prio=5 os_prio=0 tid=0x00007fb91498d000 nid=0x474a runnable [0x00007fb9065fe000] java.lang.Thread.State: RUNNABLE at Test$2.run(Test.java:18) |
3. 内存分析
需要相应的工具进行分析了,最常用的自然就是 MAT 了。
我试了一个在线工具也不错(文件大了就不适合了):
上传刚才生成的内存文件之后:
因为是内存溢出,所以主要观察下大对象:
也有相应提示,这个很有可能就是内存溢出的对象,点进去之后:
4. 建议
- 尽量不要在线程中做大量耗时的网络操作,如查询数据库(可以的话在一开始就将数据从从 DB 中查出准备好)。
- 尽可能的减少多线程竞争锁。可以将数据分段,各个线程分别读取。
- 多利用
CAS+自旋
的方式更新数据,减少锁的使用。 - 应用中加上
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp
参数,在内存溢出时至少可以拿到内存日志。 - 线程池监控。如线程池大小、队列大小、最大线程数等数据,可提前做好预估。
- JVM 监控,可以看到堆内存的涨幅趋势,GC 曲线等数据,也可以提前做好准备。
1 2 3 4 |
-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/xx/Documents |
JVM 出现 OOM 的时候自动 dump 内存到 /Users/xx/Documents
(不配路径则会生成在当前目录)
[source]
假笨的微信公众号,他这几年写了很多 JVM 的文章,也做了很多方便我们调试的小工具,相信能帮到你,特别是想深入研究 JVM 的同学。同时,他公众号还记录了自己成为 JVM 专家的点滴历程,比如我是如何走时 JVM 这条贼船