本文所述的方法操作简单,但要能快速定位问题,需要一些重复练习和实践。
排查线程死循环
思路
通过top -H -p $pid列出进程的所有线程,默认是按cpu排序的(可按<或>调整排序字段,我常用的还有TIME+);如果有死循环线程,在未休眠或挂起的情况下(占用一核),将会显示在最上方
找到线程,通过jstack $pid | grep $thread_id -a30定位到堆栈
注:jstack中记录的thread_id是16进制,top拿到的线程ID是10进制,grep时需自行转化
编写脚本
strong_tip
="\033[44m\033[37m"
warn_tip
=$strong_tip"\033[05m"
error_tip
="\033[41m\033[37m"
tip_ending
="\033[0m"
pid
=$1
if [ "$pid" == "" ]; then
echo -ne
"$warn_tip Please input $tip_ending$strong_tip pid of java process: $tip_ending"
read pid
fi
if [ "$pid" == "" ]; then
echo -e
"$error_tip Usage: Unknown pid of java process. $tip_ending"
exit 2
fi
echo "***********************************************************************************************************************"
echo -e
"\t[" $(date -d "today" +"%Y-%m-%d %H:%M:%S") "] $strong_tip show thread detail of the process " $pid "$tip_ending: "
echo "***********************************************************************************************************************"
top -Hn1 p
$pid
tid
=$2
if [ "$tid" == "" ]; then
echo -ne
"$warn_tip Please input $tip_ending$strong_tip tid of java thread: $tip_ending"
read tid
fi
if [ "$tid" == "" ]; then
echo -e
"$error_tip Usage: Unknown tid of java thread. $tip_ending"
exit 2
fi
tid_hex
=`printf "%x" $tid`
echo -e
"\n*****************************************************************"
echo -e
"\t[" $(date -d "today" +"%Y-%m-%d %H:%M:%S") "] $strong_tip finding the stack of tid " $tid "(hex:" $tid_hex ") $tip_ending"
echo -e
"*****************************************************************"
jstack
$pid | grep $tid_hex -a30
&
排查线程挂起阻塞等异常情况
上面的脚本仅适合简单的、可快速通过cpu资源异常定位的情况。但一般业务中还有其他情况:
线程可能是间歇休眠的、难以捕捉线程阻塞、导致线程池占满CPU高的都是gc线程,看出gc很忙,但不知道哪个业务一直在频繁请求内存
针对上面的情况,第一种方法就不管用了。 我采用的方案是使用jstack导出完整的栈,然后重点排查BLOCKED的线程,也可以统计某一类线程的数量,看有否异常。
命令很简单:jstack -l ${pid} | tee -a $file 导出的文件内容如下图: 重点关注java.lan.Thread.State,等待、阻塞的线程,可以进一步通过waiting on后面的描述、tid进行追溯。
还有个小策略,对比先后导出的两个栈文件,看重复的有哪些内容,往往就是高频操作、或阻塞的逻辑。
囿于时间关系,没有整理案例,有问题可以留言交流。