Java线程CPU飙高、僵尸进程和磁盘满的排查技巧
线上故障就像埋在系统里的“定时炸弹”——CPU突然飙高导致服务卡顿、海量日志找不到异常、磁盘满了直接宕机,每一种都能让程序员头皮发麻。
不用瞎试错,不用靠经验瞎猜,今天整理了4类高频线上故障的排查套路,附实操命令,从定位到解决一步到位,收藏起来,下次故障直接抄作业!
一、CPU飙高:快速定位到具体Java线程(实操拉满)
线上服务突然卡顿、接口响应超时,大概率是CPU飙高惹的祸。很多新手遇到这种情况,只会用top命令看一眼CPU占用,却不知道怎么定位到具体的代码线程,最后只能瞎折腾。
分享一套极简排查流程,从定位进程到找到异常线程,全程只需几行命令,新手也能快速上手👇
1. 第一步:定位CPU占用最高的进程
先用top命令查看系统CPU使用情况,重点关注%CPU列,找到占用率最高的进程(PID):
top
- 操作技巧:输入top后,按「P」键(大写),可以按CPU占用率从高到低排序,一眼就能找到“罪魁祸首”进程。
- 关键说明:如果是Java服务,进程名一般是java,记住对应的PID(比如12345),后续用得上。
2. 第二步:定位进程中CPU占用最高的线程
找到异常进程后,用top -H -p 命令,定位该进程下占用CPU最高的线程(TID):
top -H -p 12345 # 12345替换为第一步找到的PID
- 核心作用:-H参数会将进程拆分到线程级别,按「P」键排序后,就能找到占用CPU最高的线程ID(TID,比如12346)。
3. 第三步:将线程ID转换为16进制(关键一步)
因为Java的jstack日志中,线程ID是16进制的,所以需要将第二步找到的TID(十进制)转换为16进制:
printf "%x\n" 12346 # 12346替换为第二步找到的TID
- 示例:假设转换后得到的16进制线程ID是303a(注意小写,jstack日志中是小写)。
4. 第四步:用jstack打印日志,定位异常线程
最后用jstack命令打印该进程的线程日志,并用grep过滤出目标线程,就能找到异常代码的堆栈信息:
jstack 12345 | grep 303a -A 20 # 12345是进程PID,303a是16进制线程ID,-A 20表示显示后续20行
- 关键解读:日志中会显示该线程正在执行的代码行、方法名,顺着堆栈信息找下去,就能快速定位到导致CPU飙高的具体代码(比如死循环、频繁GC等)。
小提示:排查完成后,记得点赞收藏,下次遇到CPU飙高,直接按这个流程来,不用再翻资料!
二、日志分析:海量日志中快速定位异常(grep组合拳)
线上服务报错,日志文件动辄几百M、几个G,直接打开查找异常信息,不仅耗时,还容易看漏。
分享3个最实用的grep组合命令,帮你快速从海量日志中筛选出异常信息,效率翻倍👇
1. 基础用法:筛选包含指定关键词的日志
最常用的场景:查找包含“Error”“Exception”等异常关键词的日志,快速定位报错位置:
# 筛选包含Error的日志,显示行号 grep -n "Error" app.log # 筛选包含Exception的日志,忽略大小写(比如Exception、exception都能匹配) grep -i "Exception" app.log
2. 进阶用法:筛选指定时间段的日志
很多时候,我们知道故障发生的大致时间段,只需筛选该时间段内的日志,缩小查找范围:
# 假设日志格式是 2026-02-10 14:30:00 错误信息,筛选14:30-14:40之间的Error日志 grep "2026-02-10 14:3[0-4]" app.log | grep "Error"
- 灵活调整:根据自己项目的日志格式,修改时间匹配规则(比如yyyy-MM-dd HH:mm:ss、MM-dd HH:mm等)。
3. 高阶用法:筛选异常日志并输出到文件
如果异常日志较多,可将筛选结果输出到单独的文件中,方便后续分析(避免反复执行命令):
# 筛选近1小时内的Exception日志,输出到error.log文件中 grep "2026-02-10 13:" app.log | grep -i "Exception" > error.log
小技巧:如果日志是滚动日志(比如app.log.1、app.log.2),可以用grep “关键词” app.log* 批量筛选所有日志文件。
三、僵尸进程:产生原因+清理方法(避免占用系统资源)
僵尸进程(Zombie)是线上常见的“隐形杀手”——它本身不占用CPU和内存,但会占用系统进程号(PID),如果大量堆积,会导致系统无法创建新进程,最终引发服务异常。
1. 先搞懂:僵尸进程是什么?怎么产生?
- 定义:僵尸进程是指子进程已经终止,但父进程没有调用wait()或waitpid()函数回收子进程资源,导致子进程残留的“空壳进程”。
- 核心原因:父进程异常退出、父进程逻辑缺陷(未回收子进程)、子进程执行时间过短,父进程还没来得及回收。
2. 第一步:查找系统中的僵尸进程
用ps命令筛选出僵尸进程,僵尸进程的状态标记为「Z」:
ps -ef | grep defunct # defunct是僵尸进程的标识
# 或者更精准的筛选
ps aux | awk '{if($8=="Z") print $0}'
- 输出解读:筛选结果中,STAT列显示为Z的,就是僵尸进程,记住对应的PID和父进程PID(PPID)。
3. 第二步:清理僵尸进程(两种方法,按需选择)
清理僵尸进程的核心是“回收子进程资源”,优先用温和方法,避免影响正常服务:
方法1:重启父进程(推荐,温和安全)
僵尸进程是父进程未回收导致的,重启父进程后,系统会自动回收其下属的僵尸进程:
# 先查看父进程名称(PPID是父进程ID) ps -ef | grep 1234 # 1234是父进程PPID # 重启父进程(根据自己的服务启动方式调整) systemctl restart 服务名 # 比如systemctl restart java-service
方法2:强制杀死父进程(紧急情况使用)
如果父进程无法重启,可强制杀死父进程,系统会将僵尸进程托管给init进程(PID=1),init进程会自动回收僵尸进程:
kill -9 1234 # 1234是父进程PPID,谨慎使用!
警告:强制杀死父进程会导致父进程对应的服务中断,仅在紧急情况下使用,提前做好备份。
四、磁盘满:inode耗尽与文件删除后空间未释放(坑点规避)
线上服务突然宕机,登录服务器后发现磁盘满了(df -h查看使用率100%),但删除大文件后,磁盘空间还是没释放——这两个坑,很多程序员都踩过。
1. 坑点1:文件删除后,空间未释放(原因+解决)
核心原因:
删除的文件正在被进程占用(比如日志文件被Java进程占用),此时rm命令只是删除了文件的目录项,文件的实际内容还在磁盘中,空间不会释放。
排查+解决步骤:
查找被占用的已删除文件:
lsof | grep deleted # 筛选出已删除但仍被进程占用的文件
找到对应的进程(PID),重启该进程(释放文件占用):
systemctl restart 服务名 # 比如重启Java服务,释放日志文件占用
补充说明:如果无法重启进程,可通过echo “” > 文件名 的方式清空文件(避免删除文件导致的占用问题):
echo "" > app.log # 清空日志文件,释放空间,且不影响进程占用
2. 坑点2:inode耗尽(磁盘有空间,但无法创建文件)
核心原因:
磁盘的inode节点耗尽了——inode是文件的索引,每个文件对应一个inode,即使磁盘有剩余空间,inode耗尽后,也无法创建新文件(报错:no space left on device)。
排查+解决步骤:
查看inode使用情况:
df -i # 查看各分区inode使用率,Ifree列是剩余inode数量
定位inode占用最多的目录(找到大量小文件的目录):
# 从根目录开始查找,统计每个目录的inode数量 for i in /*; do echo $i; find $i | wc -l; done
解决方法:删除目录下的大量小文件(比如日志碎片、临时文件),释放inode:
# 批量删除指定目录下的小文件(谨慎操作,确认文件可删除) rm -rf /tmp/* # 比如删除/tmp目录下的临时文件
预防建议:定期清理临时文件、日志碎片,避免小文件堆积导致inode耗尽,可写定时脚本自动清理。
五、总结
线上故障排查的核心不是“瞎试错”,而是“找对逻辑、用对命令”——CPU飙高找线程、日志繁杂用grep、僵尸进程清父进程、磁盘满分两种坑点,按本文的套路来,大部分高频故障都能在10分钟内定位并解决。
以上就是Java线程CPU飙高、僵尸进程和磁盘满的排查技巧的详细内容,更多关于Java线程CPU飙高、僵尸进程和磁盘满的资料请关注脚本之家其它相关文章!
相关文章
IDEA Maven Dependencies出现红色波浪线的原因与解决
在使用 IntelliJ IDEA 开发 Java 项目时,尤其是基于 Maven 的项目,开发者可能会遇到 Maven Dependencies 中出现红色波浪线的问题,本文我们就来看看这一现象的原因与解决吧2025-06-06


最新评论