谈谈Tomcat占用cpu高的问题
目录
问题现场
测试环境tomcat进程占用CPU一直持续99%,但是通过jstack查看log,也没有任何线程死锁的情况。
此时通过$catalina_home/bin/shutdown.sh脚本无法正常停止tomcat。
这是什么原因?
线程死锁 vs 线程死循环
验证线程死锁不会导致CPU持续高负载
// 验证线程死锁是否会导致CPU占用率一直居高不下
public class LockTest {
private Object alock = new Object(); // a锁
private Object block = new Object(); // b锁
public static void main(String[] args) {
new LockTest().start();
}
private void start() {
new Thread(new ThreadA()).start();
new Thread(new ThreadB()).start();
}
private void sleepOfSeconds(int seconds) {
try {
Thread.sleep(seconds * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 在线程A中先获取a锁,再获取b锁
class ThreadA implements Runnable {
private String name = "ThreadA";
@Override
public void run() {
synchronized (alock) {
System.out.println(name + " get alock");
sleepOfSeconds(50);
}
System.out.println(name + " release alock");
System.out.println(name + " waiting block");
synchronized (block) {
System.out.println(name + " get block");
}
System.out.println(name + " release block");
}
}
// 在线程B中先获取b锁,再获取a锁
class ThreadB implements Runnable {
private String name = "ThreadB";
@Override
public void run() {
synchronized (block) {
System.out.println(name + " get block");
sleepOfSeconds(10);
}
System.out.println(name + " release block");
System.out.println(name + " waiting alock");
synchronized (alock) {
System.out.println(name + " get alock");
}
System.out.println(name + " release alock");
}
}
}
线程进入死锁状态时,不会导致CPU持续高负载,实际上当线程进入死锁之后是等待获取对象所被执行,此时CPU是空闲的。
导致CPU负载持续高的原因是线程进入了死循环,导致CPU持续在工作,此时线程的状态应该是Runnable,而不是Blocked。
排查Java进程导致CPU持续高的方法
整体思路:
1.先找到占用CPU比较高的进程ID
2.再找出该进程下CPU占用比较高的线程ID,并输出该线程ID的十六进制值
3.使用jstack
命令查看并过滤出第2步中得到的线程ID对应堆栈信息,从而确定CPU占用高的地方在哪里
在Linux环境下,通过如下步骤可以实现对Java进程CPU持续高负载的问题排查:
- 通过
jps
命令找到Java进程ID,并使用top
命令确定CPU占用高的进程是否为jps
命令找到的Java进程。 - 通过
ps -mp pid -o THREAD,tid,time
命令查看进程的线程列表,找到CPU占用最高的线程ID,并使用printf "%x\n" tid
命令输出线程ID的16进制格式:tid_hex 。 - 通过
jstack pid |grep tid_hex -A 30
查看线程堆栈信息,找到处于Runnable
状态的线程中执行的代码片段,就可以分析出对应导致线程死循环的原因了。
可以将上述命令整理成一个脚本工具,用于临时排查CPU高的问题,如下:
#!/bin/bash
# Desc: 查看Java进程CPU使用率高的线程堆栈信息
# Name: show_java_process_thread_stack.sh
echo "Show java process thread stack"
pid=$1
if [ ! "$pid" ]; then
echo "Usage: sh $0 pid"
echo " e.g: sh $0 1234"
echo ""
exit 1
fi
top -H -p "$pid"|head -20
echo ""
top_thread_id=`top -H -p $pid|head -8|awk '/java/{print $2}'`
#echo "top cpu thread: $top_thread_id"
thread_id_hex=`printf "%x" "$top_thread_id"`
#echo "$thread_id_hex"
jstack "$pid"|grep "$thread_id_hex" -A 100 > jstack_tmp
cat jstack_tmp
rm -rf jstack_tmp
#echo "Done."
Tomcat的CPU占用高的原因总结
- 线程死锁和线程死循环不是一个概念,千万不要弄错。
- 通常来讲,对于部署到Tomcat中的应用程序,排除程序代码进入死循环的原因之外,会导致Tomcat进程CPU持续高负载的可能因素是存在大量的TCP连接请求(并发很大)。
- 由于应用程序出现堆内存空间不够用导致频繁GC,也会导致CPU使用率高。
- 如果应用日志输出非常频繁,也会导致CPU使用率持续高。
【参考】
https://www.jianshu.com/p/3160ba8e150d 记一次tomcat cpu占用率过高的问题排查
http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html 线上应用故障排查之一:高CPU占用
作者:编程随笔
出处:http://www.cnblogs.com/nuccch/
声明:本文版权归作者和博客园共有,欢迎转载,但请在文章页面明显位置给出原文连接。
分类:
Middleware/Tomcat
, 踩过的坑
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通