找出某个Java进程中最耗费CPU的Java线程
找出某个Java进程中最耗费CPU的Java线程并定位堆栈信息,用到的命令有:ps、top、printf、jstack、grep。
# 问题原因
现实企业级Java应用开发、维护中,有时候我们会碰到下面这些问题:
- OutOfMemoryError,内存不足
- 内存泄露
- 线程死锁
- 锁争用(Lock Contention)
- Java进程消耗CPU过高 ......
这些问题在日常开发、维护中可能被很多人忽视(比如有的人遇到上面的问题只是重启服务器或者调大内存,而不会深究问题根源),但能够理解并解决这些问题是Java程序员进阶的必备要求。
# 排查步骤
# jps
jps主要用来输出JVM中运行的进程状态信息。语法格式如下:
jps [options] [hostid]
如果不指定hostid就默认为当前主机或服务器。
命令行参数选项说明如下:
-q 不输出类名,Jar名和传入main方法的参数
-m 输出传入main方法的参数
-l 输出main类或Jar的全限名
-v 输出传入JVM的参数
2
3
4
5
6
7
比如下面:
root@ubuntu:/# jps -m -l
2458 org.artifactory.standalone.main.Main /usr/local/artifactory-2.2.5/etc/jetty.xml
29920 com.sun.tools.hat.Main -port 9998 /tmp/dump.dat
3149 org.apache.catalina.startup.Bootstrap start
30972 sun.tools.jps.Jps -m -l
8247 org.apache.catalina.startup.Bootstrap start
25687 com.sun.tools.hat.Main -port 9999 dump.dat
21711 mrf-center.jar
2
3
4
5
6
7
8
# jstack
jstack主要用来查看某个Java进程内的线程堆栈信息。语法格式如下:
jstack [option] pid
jstack [option] executable core
jstack [option] [server-id@]remote-hostname-or-ip
2
3
命令行参数选项说明如下:
# 会打印出额外的锁信息
# 在发生死锁时可以用 jstack -l pid 来观察锁持有情况
# -m mixed mode,不仅会输出Java堆栈信息,还会输出C/C++堆栈信息(比如Native方法)
-l long listings
2
3
4
jstack可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多。
下面我们来一个实例找出某个Java进程中最耗费CPU的Java线程并定位堆栈信息,用到的命令有ps、top、printf、jstack、grep。
第一步: 先找出Java进程ID,服务器上的Java应用名称为 wordcount.jar:
[root@storm-master home] ps -ef | grep wordcount
root 2860 2547 13 02:09 pts/0 00:02:03 java -jar wordcount.jar /home/input 3
2
得到进程ID为 2860 。
第二步: 找出该进程内最耗费CPU的线程,可以使用如下3个命令,这里我们使用第3个命令得出如下结果:
ps -Lfp pid : 即 ps -Lfp 2860
ps -mp pid -o THREAD, tid, time :即 ps -mp 2860 -o THREAD,tid,time
top -Hp pid: 即 top -Hp 2860
这里用第三个命令:top -Hp 2860
top -Hp 2860
输出如下:
TIME列就是各个Java线程耗费的CPU时间,显然CPU时间最长的是ID为2968的线程,用
printf "%x\n" 2968
得到2968的十六进制值为b98,下面会用到。
第三步: 终于轮到jstack上场了,它用来输出进程2860的堆栈信息,然后根据线程ID的十六进制值grep,如下:
[root@storm-master home] jstack 2860 | grep b98
"SessionTracker" prio=10 tid=0x00007f55a44e4800 nid=0xb53 in Object.wait() [0x00007f558e06c000
2
可以看到CPU消耗在SessionTracker这个类的Object.wait(),于是就能很容易的定位到相关的代码了。
当然,可以 jstack -l pid > /tmp/thread.txt
在导出的文件中搜索,就可以定位到具体的线程,类。
# 其他问题排查
查看某进程及某线程占用 CPU 的例子
jps
: 列出 java 进程,找到 pid.pidstat -p pid -u 1 3 -u -t
: 查看 pid 的进程所有线程的 cpu 使用情况.jstack -l pid > /tmp/thread.txt
: 导出指定 Java 应用的所有线程.
然后查看 nid=xxx
(即第二步里线程号的线程),即可定位到某段代码.
查看某进程及某线程占用 IO 的例子
jps
: 列出 java 进程,找到 pid.pidstat -p pid -u 1 3 -d -t
: 查看 pid 的进程所有线程的 IO 使用情况.jstack -l pid > /tmp/thread.txt
: 导出指定 Java 应用的所有线程.
然后查看 nid=xxx
(即第二步里线程号的线程),即可定位到某段代码.
# 相关脚本
阿里开源的 Arthas
看了下有位大神提个 issue (opens new window) , 推荐了个自动化脚本, 亲测更好用点. 这样子就可以免去上面的一步一步地查找和计算了. 所以, 这里也直接引用这个工具, 有需要的可以用下.
show-busy-java-threads (opens new window)