一文搞懂CPU飙升100%怎么办?看完还不会你就打我

一文搞懂CPU使用率飙升至100%的原因及解决方案

前言

作为一名 Java 后端程序员,经常会面临各种各样的线上问题,能够快速、准确地定位并解决问题,是迈向中高级开发的必经之路。今天我们就来聊一下CPU使用率飙升至100%的问题,这种情况不仅会导致系统响应变慢,甚至可能引发服务不可用。本文将结合实际案例,详细分析CPU使用率飙升的原因,并提供相应的排查和解决方案。

一、CPU使用率为什么会飙升?

  1. 无限循环或高频计算:这是最常见的原因之一。某些代码逻辑可能因为错误或设计缺陷,陷入无限循环或执行高频计算任务,导致线程持续占用CPU。
  2. 锁争用:当多个线程竞争同一资源时,可能会导致锁争用。线程反复尝试获取锁,却无法成功,从而产生大量CPU消耗。
  3. 垃圾回收(GC):如果JVM的堆内存设置不合理,可能会导致垃圾回收线程频繁工作。频繁的GC不仅会占用大量CPU资源,还可能导致系统响应变慢。

二、CPU使用率飙升的排查步骤

0. 测试CPU飙升的代码

类名:ProjectController

**这个代码中,while (true) 导致了一个无限循环,线程会持续计算平方根,从而占用大量CPU资源**

@GetMapping("/testCpu")
public void testCpu() {
    while (true) {
        double result = 0;
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            result += Math.sqrt(i);
        }
    }
}

1. 确认占用CPU最高的进程

当发现CPU使用率飙升时,首先需要确认是哪个进程导致的。可以使用top命令查看系统中CPU占用最高的进程。

top命令的输出显示了系统的实时性能状况,包括CPU、内存、交换空间使用情况以及各个进程的资源占用情况。

执行命令:top
输出以下2部分内容:

第一部分:系统的总体状态,包括CPU、内存、交换空间的使用情况,以及进程数量和负载。
top - 23:02:58 up 222 days,  6:48,  1 user,  load average: 0.64, 0.19, 0.10
Tasks: 173 total,   2 running, 171 sleeping,   0 stopped,   0 zombie
%Cpu(s): 50.1 us,  0.0 sy,  0.0 ni, 49.8 id,  0.0 wa,  0.0 hi,  0.2 si,  0.0 st
KiB Mem :  3861292 total,  1966080 free,  1289584 used,   605628 buff/cache
KiB Swap:  1679356 total,  1617148 free,    62208 used.  2155704 avail Mem 

第二部分:各个进程的详细信息,包括CPU占用、内存占用等。可以看到最占用资源的进程是PID为17329的Java进程,CPU占用率高达100.3%。
   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                    
 17329 usr       20   0 4894520   1.0g  13972 S 100.3 26.6   2:43.30 java                                                                                                                                       
 17942 usr       20   0  162108   2332   1580 R   0.3  0.1   0:00.07 top                                                                                                                                        
     1 root      20   0  191204   3428   2048 S   0.0  0.1   4:15.67 systemd    

第一部分关键参数详解:

%Cpu(s): 50.1 us, 0.0 sy, 0.0 ni, 49.8 id, 0.0 wa, 0.0 hi, 0.2 si, 0.0 st
这些数值表示CPU的使用情况:
us:50.1%的CPU时间被用户空间的进程(用户程序)占用
sy:0.0%的CPU时间被内核空间的进程占用
ni:0.0%的CPU时间被改变优先级的进程占用(优先级调整)
id:49.8%的CPU时间处于空闲状态
wa:0.0%的CPU时间等待I/O操作
hi:0.0%的CPU时间用于硬件中断
si:0.2%的CPU时间用于软件中断
st:0.0%的CPU时间被虚拟化环境占用(如果是虚拟机中运行的话)

KiB Mem : 3861292 total, 1966080 free, 1289584 used, 605628 buff/cache
3861292 total:总内存
1966080 free:剩余空闲内存
1289584 used:已使用的内存
605628 buff/cache:用于缓存和缓冲区的内存

第二部分关键参数详解:

 1. PID:进程ID。每个进程都有一个唯一的PID,用来标识它。 
 2. USER:进程的所属用户。
 3. %CPU:进程占用的CPU百分比。(100.3:Java进程占用了100.3%的CPU资源)
 4. %MEM:进程占用的内存百分比。(26.6:Java进程占用了系统内存的26.6%)
 5. TIME+:进程使用的CPU时间。(2:43.30:Java进程使用了2分钟43秒30毫秒的CPU时间)
 6. COMMAND:进程的命令名称或可执行文件名。(java:表示进程是由Java应用程序启动的,top:表示这个进程是top命令本身)

2. 找到进程下占用CPU最高的线程

确认是Java应用导致CPU占用过高后,需要进一步了解是哪个线程导致的。可以使用top -Hp命令查看进程下每个线程的CPU使用情况:

执行命令:top -Hp 17329

注:-H 参数会显示该进程中每个线程的 CPU 使用情况。
    17329就是步骤1的输出结果中第一行的进程id。
    
会输出以下内容:

top - 23:03:16 up 222 days,  6:49,  1 user,  load average: 0.74, 0.25, 0.12
Tasks: 174 total,   1 running, 173 sleeping,   0 stopped,   0 zombie
top - 23:04:09 up 222 days,  6:49,  1 user,  load average: 0.90, 0.37, 0.17
Threads:  98 total,   1 running,  97 sleeping,   0 stopped,   0 zombie
%Cpu(s): 50.2 us,  0.0 sy,  0.0 ni, 49.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3861292 total,  1965988 free,  1289672 used,   605632 buff/cache
KiB Swap:  1679356 total,  1617148 free,    62208 used.  2155616 avail Mem 

   PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                                                                     
 17419 usr       20   0 4894520   1.0g  13972 R 99.7 26.6   2:05.90 http-nio-9526-e                                                                                                                             
 17346 usr       20   0 4894520   1.0g  13972 S  0.3 26.6   0:00.45 mysql-cj-abando                                                                                                                             
 17329 usr       20   0 4894520   1.0g  13972 S  0.0 26.6   0:00.00 java                                                                                                                                        
 17330 usr       20   0 4894520   1.0g  13972 S  0.0 26.6   0:17.01 java                                                                                                                                        
 17331 usr       20   0 4894520   1.0g  13972 S  0.0 26.6   0:01.06 java                                                                                                                                        
 17332 usr       20   0 4894520   1.0g  13972 S  0.0 26.6   0:01.15 java
 
可以看到,占用CPU最高的是线程id为17419的线程。

3. 将线程id转换为16进制

Java 的 jstack 工具输出的线程 ID 是十六进制格式,而 top 中显示的是十进制 ID。因此需要将线程 ID 转换为十六进制格式,以便后续查看线程堆栈信息。

 执行命令:printf "%x\n" 17419
 输出以下内容:
 440b
 
 注:该命令会输出对应的十六进制线程 ID,其中 17419top 中显示的高 CPU 占用的线程 ID。

4. 打印Java 应用的线程堆栈信息

使用jstack工具获取Java进程的线程堆栈信息,找出导致高CPU占用的线程及其运行的代码。

执行命令:jstack 17329 | grep "440b" -A10 --color
输出以下内容:

"http-nio-9526-exec-1" #90 daemon prio=5 os_prio=0 tid=0x00007f4684cac800 nid=0x440b runnable [0x00007f46240ac000]
   java.lang.Thread.State: RUNNABLE
        at com.test.controller.ProjectController.testCpu(ProjectController.java:45)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)
        
注:该命令会显示该线程的堆栈信息,-A 10 显示十六进制 ID 匹配行之后的 10 行。

5. 分析线程堆栈

通过 jstack 的输出,你可以看到占用 CPU 的线程在执行什么操作。

根据步骤4的输出结果,可以看到第一行就打印出了问题代码:正好是我们测试CPU飙升的代码。
at com.test.controller.ProjectController.testCpu(ProjectController.java:45)

6. 解决方案

因为代码是由于高频计算导致CPU占用过高,需要优化代码。例如,可以为循环添加合理的退出条件,或者优化计算逻辑。

总结

CPU使用率飙升至100%是一个常见的问题,但通过合理的排查和优化,可以有效解决。本文介绍了CPU使用率飙升的常见原因、排查方法以及解决方案。希望本文能帮助你在遇到类似问题时,快速定位并解决。

如果你在实际工作中遇到其他问题,欢迎在评论区留言,我们一起探讨!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值