CPU100%,如何排查问题?

本文详细描述了如何通过top-c,top-HpPID,jstack和grep命令找出CPU占用最高的进程及线程,进而定位到代码问题。

解决思路

1、首先使用 top -c 找到 CPU 占用最高的进程;
2、然后使用 top -Hp PID 找到进程下 CPU 占用最高的线程 PID,并且将十进制 PID 转换成十六进制;
3、之后使用 jstack 命令导出进程快照;
4、最后用 cat 命令结合 grep 命令对十六进制线程 PID 进行过滤,可以定位到出现问题的代码。

详细过程

1)找到 CPU 占用最高的进程
使用top -c命令得到进程运行列表,然后按P按照 CPU 占用率排序;
得到排在第一的进程的 PID:18412。
请添加图片描述

2)找到 CPU 占用最高的线程

  • 使用top -Hp 18412命令得到这个进程下的线程列表,然后按P按照 CPU 占用率排序;
  • 得到排在第一的线程的 PID:18413;
    请添加图片描述
  • 将十进制 PID 转换为十六进制得到47ED。
    请添加图片描述

3)导出进程快照并定位问题

  • 使用jstack命令导出进程快照;
jstack -l 18412 > ./18412.stack
  • 根据第二步得到的小写的十六进制线程 PID 使用 grep 命令进行查找;
cat 18412.stack | grep '47ed' -C 8
  • 从查询得到的堆栈信息中即可定位到有问题的代码;
    请添加图片描述
    转载:https://zhuanlan.zhihu.com/p/165209510#:~:text=%E9%A6%96%E5%85%88%E4%BD%BF%E7%94%A8%20top%20-c%20%E6%89%BE%E5%88%B0%20CPU%20%E5%8D%A0%E7%94%A8%E6%9C%80%E9%AB%98%E7%9A%84%E8%BF%9B%E7%A8%8B%EF%BC%9B%20%E7%84%B6%E5%90%8E%E4%BD%BF%E7%94%A8%20top,%E5%91%BD%E4%BB%A4%E5%AF%BC%E5%87%BA%E8%BF%9B%E7%A8%8B%E5%BF%AB%E7%85%A7%EF%BC%9B%20%E6%9C%80%E5%90%8E%E7%94%A8%20cat%20%E5%91%BD%E4%BB%A4%E7%BB%93%E5%90%88%20grep%20%E5%91%BD%E4%BB%A4%E5%AF%B9%E5%8D%81%E5%85%AD%E8%BF%9B%E5%88%B6%E7%BA%BF%E7%A8%8B%20PID%20%E8%BF%9B%E8%A1%8C%E8%BF%87%E6%BB%A4%EF%BC%8C%E5%8F%AF%E4%BB%A5%E5%AE%9A%E4%BD%8D%E5%88%B0%E5%87%BA%E7%8E%B0%E9%97%AE%E9%A2%98%E7%9A%84%E4%BB%A3%E7%A0%81%E3%80%82
### Go程序导致CPU占用100%的原因 Go程序可能导致CPU占用率达到100%,通常是因为某些逻辑设计不当或者并发模型中的错误配置。以下是几个常见原因及其对应的解决方案: #### 1. **无限循环** 如果存在未受控制的`for`循环,可能会持续运行并消耗大量CPU资源。例如,在开发环境中可能无意间编写了如下代码片段[^1]: ```go for { // 缺少退出条件或其他阻塞操作 } ``` 此类循环不会主动让出处理器时间片,从而导致单线程或多个goroutine同时执行时耗尽可用CPU。 #### 解决方案 在任何长时间运行的循环中加入适当的延迟机制来降低负载压力,比如通过调用`time.Sleep()`函数实现短暂休眠: ```go import "time" for { time.Sleep(1 * time.Second) // 添加延时减少不必要的计算负担 } ``` --- #### 2. **Select语句缺少Default分支** 当使用`select`多路复用通道通信时,如果没有设置默认选项(default case),而所有case均处于不可读/写状态,则该处会陷入忙等待模式,进而引发高CPU利用率现象[^2]。 ```go func badExample() { ch := make(chan int) go func() { close(ch) }() select {} // 此处无default路径且没有任何channel准备好接收数据 } ``` #### 改进措施 确保每一个`select`都有合理的处理方式,即使是在没有消息到来的情况下也应给予适当反馈;可以考虑增加一个超时计时器作为兜底策略之一: ```go ch := make(chan bool, 1) ticker := time.NewTicker(time.Millisecond * 500).C select { case <-ch: fmt.Println("Received from channel.") case <-ticker: fmt.Println("Timeout occurred after half second.") } defer ticker.Stop() ``` --- #### 3. **不恰当的锁竞争** 频繁获取同步原语(如互斥锁mutexes)也可能引起性能瓶颈甚至满载状况发生。假如有两个以上的goroutines争抢同一个共享变量访问权限的话,那么它们之间就会不断尝试获得对方释放出来的信号量直到成功为止——这期间耗费掉不少宝贵的时间周期[^3]。 #### 预防手段 优化算法结构尽量减少临界区内存修改次数以及缩短持有期限长度;另外还可以采用原子操作代替显式的锁定过程以提高效率: ```go var counter uint64 // 使用sync包下的atomic.AddUint64替代传统加法运算符+ newCount := atomic.AddUint64(&counter, 1) fmt.Printf("Current count is %d\n", newCount) ``` --- ### 总结 针对上述提到的各种可能性逐一排查定位具体位置之后再采取相应补救办法即可有效缓解乃至彻底消除这类问题带来的困扰。记住始终遵循良好的编程习惯对于预防潜在隐患至关重要!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值