线上CPU飙高问题排查 (Java)

近期打算整理一系列线上问题排查的文章,也做备忘用吧,虽然问题排查都有印象了,但是真遇到线上问题难免紧张,万一步骤弄错了就耽误时间了

CPU飙高问题一般通过以下几个步骤来排查

  1. 找到cpu占比高的Java进程ID,通过这一步就知道是哪个Java应用出了问题。
  2. 然后再找到该Java进程中哪些线程占用cpu时间比较高
  3. jstack -l 该Java进程到某个文件(比如/tmp/jstack.dump)。
  4. 再将步骤2得到的线程ID由10进制转换成16进制,去jstack.dump文件中看这些线程到底是执行了哪些Java代码,从而可以找出问题。

先来看看步骤1,定位cpu占比高的Java进程比较简单,可以直接通过top命令或ps命令,但由于top命令比较简单,不像ps命令有过多的参数,所以大多数会优先使用top命令。其实现在线上大多是每个服务都有单独的容器了,所以CPU飙高大部分是你的服务有问题(少数情况下有一些插件异常导致的CPU过高,也需要排查少部分情况,做到严谨)

详细描述

一:找到最耗CPU的进程

执行top -c,显示进程运行信息列表

输入P,进程按照CPU使用率排序

线上CPU飙高问题排查 (Java)_第1张图片

如图所示,最耗CPU的进程PID为32761

二:找到最耗CPU的线城

top -Hp 32761 显示一个进程的线城运行信息列表

输入P,线程按照CPU使用率排序,这里没图了,假设最耗CPU的线程PID为10704

三:查看堆栈信息,定位线程在做啥,定位对应代码

1.将线程PID转为16进制

printf "%x\n" 10704

得到对应的16进制是29d0

2. 接着查看堆栈,找到线程

jstack 32761|grep '29d0'

打印进程堆栈,通过线程ID过滤得到线程堆栈

最后根据堆栈里的信息,找到对应的代码即可


 以上几个步骤是没问题的,但是真到线上排查时显得步骤有些多,毕竟出问题的时候是要争分夺秒的,最好是通过一个脚本就能完成上述所有操作

入参只有一个就是Java的pid,如果没有入参那么默认会取最耗CPU的Java进程

#!/bin/bash
if [ -z "$1" ]; then

          ### 1.先找到消耗cpu最高的Java进程 ###

                  pid=`ps -eo pid,%cpu,cmd --sort=-%cpu | grep java | grep -v grep | head -1 | awk 'END{print $1}' `

                          if [ "$pid" =  ""  ]; then

                                            echo "无Java进程,退出。"

                                                            exit

                                                                    fi

                                                                  else

                                                                            pid=$1

                                                                          fi
curTime=$(date +%Y%m%dT%H:%M:%S)
dumpFilePath="/tmp/pid-${pid}−${curTime}.jstack"


echo -e  "java 进程ID为 $pid" > ${dumpFilePath}
ps -ef|grep ${pid} >> ${dumpFilePath}
topThreadId=`top -b -n 1 -Hp ${pid}|grep java|head -n 1|awk '{print $1}'`
cpuUsage=`top -b -n 1 -Hp ${pid}|grep java|head -n 1|awk '{print $9}'`
echo -e  "最耗cpu的使用率为 $cpuUsage">>$dumpFilePath
topThreadId16=`printf "%x" ${topThreadId}`
topThreadId16ThreadName="nid=0x${topThreadId16}"
echo -e  "最耗cpu的java线程ID 16进制为 $topThreadId16ThreadName">>$dumpFilePath
jstack -l ${pid} >>$dumpFilePath
#threadDe -e tail=`jstack ${pid}|grep ${topThreadId16} -C 10`
#echo "$threadDetail"

这样就能实现一个脚本记录所有信息了,大大节省线上排查问题的时间,也不容易看错

你可能感兴趣的:(Java,java,jvm,运维)