做为应用负责人,谁都希望自己负责的应用能够在线上跑得顺顺当当,不出任何错误,也不产生任何告警,当然这是最理想的结果,也是做为技术人员希望达到的最终效果。可是实事上应用就像小孩一样,总会在不经意间,不按你期望的结果运行,如CPU偏高、内存占用偏高、应用没有响应、应用自动挂掉等,搞得我们技术人员不是一般的头大。我本人虽然身处管理岗位,也是处理在技术的第一线,也曾碰到过各种各样的奇奇怪怪的问题,在此记录下来,分享给给大家,今天第一篇分享是关于如何排查CPU偏高的问题。
以下是一段简易的测试代码,用于模拟应用的CPU占用偏高:
public class CpuUseTest { public static void main(String[] args) { new Thread() { public void run() { int result = 0; while (true) { result++; if (result > Integer.MAX_VALUE / 2) { result = 0; } } } }.start(); } }
大家应该都知道,单个CPU在同一时刻只能够执行一个线程,如果某个线程一直无限的循环下去,其对这个CPU的占用就不会释放,会把这个CPU给占满,其它线程无法使用,如果电脑是单核的,那么这个时候就会感觉到明显的卡顿。
把这个应用给运行起来后,可以通过TOP命令查看到其CPU的占用情况如下:
可以看到其CPU占用率达到了100%,这个时候我们当然希望找出这个CPU占用偏高的代码位置,只有这样我们才能够解决这个问题。首先我们需要理解,Java是一个多线程应用,进程是由多个线程构成的,我们当前看到的是这个是这个进程的CPU占用率,因而导致这个进程CPU偏高的是其中某个或某几个线程,因而我们如果能够找到这些线程那解决问题就好办了。
在Linux环境下,可以通过ps命令(ps命令的详细使用,请通过man ps命令获取更多的帮助)查看指定进程的线程情况,如下指令:
ps -mp 26541 -o THREAD,tid,time
可以查看应用ID为26541的这个Java应用中,线程的CPU使用情况,结果如下:
可以看到进程号26541对应的线程tid为26561的线程,CPU占用率达到了99.9%,因而我们只需要找出这个线程目前正在做的事情,那就好解决问题了。Java的JDK中有包括对应的线程堆栈查看工具jstack,根据找到的线程ID 26561,就可以定位到CPU偏高的问题了。
注:通过ps命令查看到的线程ID 26561是十进制,在有的Java JDK里面的jstack命令输出的是十进制的线程ID,有的是十六进制的线程ID,这个需要使用者先通过jstack命令去查看了,如果是十六进制的线程ID,可以通过如下命令将十进制的线程ID转换为十六进程的线程ID,如这里将线程ID 26561转换为十六进制的命令如下:
fenglibin@fenglibin-HP:/home/fenglibin$ printf "%x/n" 26561 67c1
这里得到的线程ID的十六进制就是67c1,我的电脑中的jstack显示的线程号是十六进制的,因而就使用这个十六制了。通过如下命令可以找到导致当前应用CPU偏高的线程和其执行堆栈:
可以看到导致该线程CPU偏高的执行堆栈为测试代码中的第18行,也就是执行无限循环的代码块所在的位置,直此导致该应用CPU偏高的问题,被成功定位。
后续我们继续写如何定位内存溢出的问题,如果感兴趣,可以继续观注。