show-busy-java-threads.sh的解析和使用实例
·
#!/bin/bash
# =============================================================================
# 脚本名称:show-busy-java-threads.sh
# 功能描述:找出 Java 进程中 CPU 占用最高的线程,并打印这些线程的堆栈信息
# 作者:Jerry Lee
# 源码:https://github.com/oldratlee/useful-shells/blob/master/show-busy-java-threads.sh
# =============================================================================
# 获取脚本名称
PROG=`basename $0`
# -----------------------------------------------------------------------------
# 函数:usage
# 功能:显示帮助信息并退出
# 参数:$1 - 退出状态码
# -----------------------------------------------------------------------------
usage() {
cat <<EOF
Usage: ${PROG} [OPTION]...
Find out the highest cpu consumed threads of java, and print the stack of these threads.
Example: ${PROG} -c 10
Options:
-p, --pid find out the highest cpu consumed threads from the specifed java process,
default from all java process.
-c, --count set the thread count to show, default is 5
-h, --help display this help and exit
EOF
exit $1
}
# -----------------------------------------------------------------------------
# 解析命令行参数
# 使用 getopt 进行参数解析,支持短选项和长选项
# -----------------------------------------------------------------------------
ARGS=`getopt -n "$PROG" -a -o c:p:h -l count:,pid:,help -- "$@"`
[ $? -ne 0 ] && usage 1
eval set -- "${ARGS}"
# 遍历解析后的参数
while true; do
case "$1" in
-c|--count)
count="$2"
shift 2
;;
-p|--pid)
pid="$2"
shift 2
;;
-h|--help)
usage
;;
--)
shift
break
;;
esac
done
# 设置默认值:显示前 5 个线程
count=${count:-5}
# -----------------------------------------------------------------------------
# 函数:redEcho
# 功能:以红色字体输出文本(如果 stdout 是终端)
# 参数:$@ - 要输出的文本
# -----------------------------------------------------------------------------
redEcho() {
[ -c /dev/stdout ] && {
# 如果 stdout 是控制台,启用彩色输出
echo -ne "\033[1;31m"
echo -n "$@"
echo -e "\033[0m"
} || echo "$@"
}
# -----------------------------------------------------------------------------
# 检查 jstack 命令是否存在
# jstack 是 JDK 提供的线程堆栈查看工具
# -----------------------------------------------------------------------------
if ! which jstack &> /dev/null; then
[ -z "$JAVA_HOME" ] && {
redEcho "Error: jstack not found on PATH!"
exit 1
}
! [ -f "$JAVA_HOME/bin/jstack" ] && {
redEcho "Error: jstack not found on PATH and $JAVA_HOME/bin/jstack file does NOT exists!"
exit 1
}
! [ -x "$JAVA_HOME/bin/jstack" ] && {
redEcho "Error: jstack not found on PATH and $JAVA_HOME/bin/jstack is NOT executalbe!"
exit 1
}
export PATH="$JAVA_HOME/bin:$PATH"
fi
# 生成唯一标识符,用于临时文件命名
# 格式:时间戳_随机数_进程 ID
uuid=`date +%s`_${RANDOM}_$$
# -----------------------------------------------------------------------------
# 函数:cleanupWhenExit
# 功能:脚本退出时清理临时文件
# -----------------------------------------------------------------------------
cleanupWhenExit() {
rm /tmp/${uuid}_* &> /dev/null
}
# 注册退出时的清理操作
trap "cleanupWhenExit" EXIT
# -----------------------------------------------------------------------------
# 函数:printStackOfThread
# 功能:打印指定线程的堆栈信息
# 输入:通过管道读取线程信息(格式:pid threadId user pcpu)
# -----------------------------------------------------------------------------
printStackOfThread() {
while read threadLine ; do
# 解析线程信息
pid=`echo ${threadLine} | awk '{print $1}'`
threadId=`echo ${threadLine} | awk '{print $2}'`
# 将线程 ID 转换为十六进制(jstack 输出中使用十六进制)
threadId0x=`printf %x ${threadId}`
user=`echo ${threadLine} | awk '{print $3}'`
pcpu=`echo ${threadLine} | awk '{print $5}'`
# 定义 jstack 输出文件路径
jstackFile=/tmp/${uuid}_${pid}
# 如果还没有获取该进程的堆栈,则执行 jstack
[ ! -f "${jstackFile}" ] && {
jstack ${pid} > ${jstackFile} || {
redEcho "Fail to jstack java process ${pid}!"
rm ${jstackFile}
continue
}
}
# 输出线程堆栈信息
redEcho "Busy(${pcpu}%) thread(${threadId}/0x${threadId0x}) stack of java process(${pid}) under user(${user}):"
# 从 jstack 输出中提取指定线程的堆栈(从 nid=0x... 开始到空行结束)
sed "/nid=0x${threadId0x}/,/^$/p" -n ${jstackFile}
done
}
# -----------------------------------------------------------------------------
# 主流程:
# 1. 使用 ps 命令获取所有 Java 线程的 CPU 使用率
# 2. 根据是否指定 pid 进行过滤
# 3. 按 CPU 使用率降序排序
# 4. 取前 N 个线程
# 5. 打印这些线程的堆栈信息
# -----------------------------------------------------------------------------
ps -Leo pid,lwp,user,comm,pcpu --no-headers | {
# 如果没有指定 pid,获取所有 java 进程;否则获取指定 pid 的 java 进程
[ -z "${pid}" ] &&
awk '$4=="java"{print $0}' ||
awk -v "pid=${pid}" '$1==pid,$4=="java"{print $0}'
} | sort -k5 -r -n | head --lines "${count}" | printStackOfThread
📖 使用示例
1️⃣ 基础用法(显示 CPU 占用最高的 5 个线程)
./show-busy-java-threads.sh
输出示例:
Busy(45.2%) thread(12345/0x3039) stack of java process(6789) under user(root):
"main" #1 prio=5 os_prio=0 tid=0x00007f8b8c001000 nid=0x3039 runnable [0x00007f8b94a3e000]
java.lang.Thread.State: RUNNABLE
at com.example.MyClass.myMethod(MyClass.java:123)
at com.example.MyClass.anotherMethod(MyClass.java:456)
...
Busy(32.1%) thread(12346/0x303a) stack of java process(6789) under user(root):
"http-nio-8080-exec-1" #10 prio=5 os_prio=0 tid=0x00007f8b8c002000 nid=0x303a runnable [0x00007f8b94b3f000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
...
2️⃣ 显示前 10 个线程
./show-busy-java-threads.sh -c 10
# 或使用长选项
./show-busy-java-threads.sh --count 10
3️⃣ 查看指定 Java 进程的线程
# 先找到 Java 进程的 PID
ps aux | grep java
# 假设 PID 是 12345
./show-busy-java-threads.sh -p 12345
# 或使用长选项
./show-busy-java-threads.sh --pid 12345
4️⃣ 组合使用
# 查看 PID 为 12345 的 Java 进程中 CPU 占用最高的 20 个线程
./show-busy-java-threads.sh -p 12345 -c 20
5️⃣ 查看帮助信息
./show-busy-java-threads.sh -h
# 或
./show-busy-java-threads.sh --help
帮助输出:
Usage: show-busy-java-threads.sh [OPTION]...
Find out the highest cpu consumed threads of java, and print the stack of these threads.
Example: show-busy-java-threads.sh -c 10
Options:
-p, --pid find out the highest cpu consumed threads from the specifed java process,
default from all java process.
-c, --count set the thread count to show, default is 5
-h, --help display this help and exit
🔍 实际应用场景
场景 1:生产环境 CPU 100% 问题排查
# 直接运行,查看哪个 Java 线程占用 CPU 最高
./show-busy-java-threads.sh
# 如果发现是 GC 线程,可能是内存问题
# 如果发现是业务线程,可以定位到具体代码行
场景 2:特定应用性能分析
# 先找到目标应用的 PID
jps -l
# 分析该应用的线程
./show-busy-java-threads.sh -p <PID> -c 15
场景 3:定期监控(配合 watch 命令)
# 每 2 秒刷新一次
watch -n 2 './show-busy-java-threads.sh -c 3'
github: https://github.com/yikebocai/shell/blob/master/show-busy-java-threads.sh
更多推荐




所有评论(0)