【Linux高级篇】Java线程CPU飙高、僵尸进程、磁盘满?一篇吃透所有排查技巧


🍃 予枫个人主页
📚 个人专栏 : 《Java 从入门到起飞》《读研码农的干货日常

💻 Debug 这个世界,Return 更好的自己!


线上故障就像埋在系统里的"定时炸弹"------CPU突然飙高导致服务卡顿、海量日志找不到异常、磁盘满了直接宕机,每一种都能让程序员头皮发麻。

不用瞎试错,不用靠经验瞎猜,今天整理了4类高频线上故障的排查套路,附实操命令,从定位到解决一步到位,收藏起来,下次故障直接抄作业!

文章目录

一、CPU飙高:快速定位到具体Java线程(实操拉满)

线上服务突然卡顿、接口响应超时,大概率是CPU飙高惹的祸。很多新手遇到这种情况,只会用top命令看一眼CPU占用,却不知道怎么定位到具体的代码线程,最后只能瞎折腾。

分享一套极简排查流程,从定位进程到找到异常线程,全程只需几行命令,新手也能快速上手👇

1. 第一步:定位CPU占用最高的进程

先用top命令查看系统CPU使用情况,重点关注%CPU列,找到占用率最高的进程(PID):

bash 复制代码
top
  • 操作技巧:输入top后,按「P」键(大写),可以按CPU占用率从高到低排序,一眼就能找到"罪魁祸首"进程。
  • 关键说明:如果是Java服务,进程名一般是java,记住对应的PID(比如12345),后续用得上。

2. 第二步:定位进程中CPU占用最高的线程

找到异常进程后,用top -H -p 命令,定位该进程下占用CPU最高的线程(TID):

bash 复制代码
top -H -p 12345  # 12345替换为第一步找到的PID
  • 核心作用:-H参数会将进程拆分到线程级别,按「P」键排序后,就能找到占用CPU最高的线程ID(TID,比如12346)。

3. 第三步:将线程ID转换为16进制(关键一步)

因为Java的jstack日志中,线程ID是16进制的,所以需要将第二步找到的TID(十进制)转换为16进制:

bash 复制代码
printf "%x\n" 12346  # 12346替换为第二步找到的TID
  • 示例:假设转换后得到的16进制线程ID是303a(注意小写,jstack日志中是小写)。

4. 第四步:用jstack打印日志,定位异常线程

最后用jstack命令打印该进程的线程日志,并用grep过滤出目标线程,就能找到异常代码的堆栈信息:

bash 复制代码
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"等异常关键词的日志,快速定位报错位置:

bash 复制代码
# 筛选包含Error的日志,显示行号
grep -n "Error" app.log

# 筛选包含Exception的日志,忽略大小写(比如Exception、exception都能匹配)
grep -i "Exception" app.log

2. 进阶用法:筛选指定时间段的日志

很多时候,我们知道故障发生的大致时间段,只需筛选该时间段内的日志,缩小查找范围:

bash 复制代码
# 假设日志格式是 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. 高阶用法:筛选异常日志并输出到文件

如果异常日志较多,可将筛选结果输出到单独的文件中,方便后续分析(避免反复执行命令):

bash 复制代码
# 筛选近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」:

bash 复制代码
ps -ef | grep defunct  # defunct是僵尸进程的标识
# 或者更精准的筛选
ps aux | awk '{if($8=="Z") print $0}'
  • 输出解读:筛选结果中,STAT列显示为Z的,就是僵尸进程,记住对应的PID和父进程PID(PPID)。

3. 第二步:清理僵尸进程(两种方法,按需选择)

清理僵尸进程的核心是"回收子进程资源",优先用温和方法,避免影响正常服务:

方法1:重启父进程(推荐,温和安全)

僵尸进程是父进程未回收导致的,重启父进程后,系统会自动回收其下属的僵尸进程:

bash 复制代码
# 先查看父进程名称(PPID是父进程ID)
ps -ef | grep 1234  # 1234是父进程PPID
# 重启父进程(根据自己的服务启动方式调整)
systemctl restart 服务名  # 比如systemctl restart java-service

方法2:强制杀死父进程(紧急情况使用)

如果父进程无法重启,可强制杀死父进程,系统会将僵尸进程托管给init进程(PID=1),init进程会自动回收僵尸进程:

bash 复制代码
kill -9 1234  # 1234是父进程PPID,谨慎使用!

⚠️ 警告:强制杀死父进程会导致父进程对应的服务中断,仅在紧急情况下使用,提前做好备份。

四、磁盘满:inode耗尽与文件删除后空间未释放(坑点规避)

线上服务突然宕机,登录服务器后发现磁盘满了(df -h查看使用率100%),但删除大文件后,磁盘空间还是没释放------这两个坑,很多程序员都踩过。

1. 坑点1:文件删除后,空间未释放(原因+解决)

核心原因:

删除的文件正在被进程占用(比如日志文件被Java进程占用),此时rm命令只是删除了文件的目录项,文件的实际内容还在磁盘中,空间不会释放。

排查+解决步骤:

  1. 查找被占用的已删除文件:

    bash 复制代码
    lsof | grep deleted  # 筛选出已删除但仍被进程占用的文件
  2. 找到对应的进程(PID),重启该进程(释放文件占用):

    bash 复制代码
    systemctl restart 服务名  # 比如重启Java服务,释放日志文件占用
  • 补充说明:如果无法重启进程,可通过echo "" > 文件名 的方式清空文件(避免删除文件导致的占用问题):

    bash 复制代码
    echo "" > app.log  # 清空日志文件,释放空间,且不影响进程占用

2. 坑点2:inode耗尽(磁盘有空间,但无法创建文件)

核心原因:

磁盘的inode节点耗尽了------inode是文件的索引,每个文件对应一个inode,即使磁盘有剩余空间,inode耗尽后,也无法创建新文件(报错:no space left on device)。

排查+解决步骤:

  1. 查看inode使用情况:

    bash 复制代码
    df -i  # 查看各分区inode使用率,Ifree列是剩余inode数量
  2. 定位inode占用最多的目录(找到大量小文件的目录):

    bash 复制代码
    # 从根目录开始查找,统计每个目录的inode数量
    for i in /*; do echo $i; find $i | wc -l; done
  3. 解决方法:删除目录下的大量小文件(比如日志碎片、临时文件),释放inode:

    bash 复制代码
    # 批量删除指定目录下的小文件(谨慎操作,确认文件可删除)
    rm -rf /tmp/*  # 比如删除/tmp目录下的临时文件

💡 预防建议:定期清理临时文件、日志碎片,避免小文件堆积导致inode耗尽,可写定时脚本自动清理。

五、总结

线上故障排查的核心不是"瞎试错",而是"找对逻辑、用对命令"------CPU飙高找线程、日志繁杂用grep、僵尸进程清父进程、磁盘满分两种坑点,按本文的套路来,大部分高频故障都能在10分钟内定位并解决。

本文所有命令都经过线上实操验证,新手可以直接复制使用,建议收藏起来,下次遇到故障不用再翻资料、问同事。

相关推荐
没有bug.的程序员10 天前
Spring Boot 与 Sleuth:分布式链路追踪的集成、原理与线上故障排查实战
java·spring boot·分布式·后端·分布式链路追踪·sleuth·线上故障排查