重点问题!CPU利用率过高排查思路

1,256 阅读3分钟

CPU利用率高怎么办?如何排查和解决

这是一个常见的面试问题,也是线上常遇到的问题之一。遇到线上服务器异常告警,我们的首要目标不是定位问题,而是尽快恢复服务可用,消除影响。

往往这种时候不能一次性定位并解决问题,如果服务不可用我们要优先让服务可用,如果有数据异常我们要优先修复数据,也可能对此有应急方案,优先让服务恢复正常是重中之重。然后我们可以尽量去保留现场信息,比如内存dump、mysql log、gc log等,事后再去定位、解决问题和复盘。

问题场景

通常会收到监控系统告警,相关服务器负载异常,cpu使用率过高

原因:可能是业务代码死循环、GC频繁、线程阻塞等

模拟问题

为了在测试环境模拟这种情况,这里以一个简单的死循环为例子。具体操作如下:

1.可以直接在服务器创建文件

touch Test.java

2.然后编辑Test.java文件

vi Test.java

输入如下代码,然后wq保存。

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            int a = 3;
            while (true) {
                if (a == 3) {
                    a = a / 2;
                } else {
                    a = 3;
                }
            }
        });
        thread.start();
    }
}

3.命令行编译生成 Test.class文件。

javac Test.java

4.命令行执行测试文件。

java Test

问题排查

1.执行top 命令查看占用cpu最多的Java进程。

top

2.根据pid 找到对应cpu占用最多的Java进程。

top -Hp 4861

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

[root@node1 ~]# printf '%x\n' 4861
12fd

4.通过jstack 命令找到对应问题现场堆栈信息。

root@node1 ~]# jstack 4851|grep 12fd -C 10
2022-06-07 19:01:25
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.281-b09 mixed mode):

"Attach Listener" #10 daemon prio=9 os_prio=0 tid=0x00007fe7c8001000 nid=0x14fa waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #9 prio=5 os_prio=0 tid=0x00007fe7f0009800 nid=0x149a waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-0" #8 prio=5 os_prio=0 tid=0x00007fe7f00f6000 nid=0x12fd runnable [0x00007fe7f411a000]
   java.lang.Thread.State: RUNNABLE
	at Test.lambda$main$0(Test.java:7)
	at Test$$Lambda$1/471910020.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)

"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007fe7f00b6000 nid=0x14a1 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007fe7f00b3800 nid=0x14a0 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

可以看到,问题线程处于runnable状态,可以对应定位到问题代码Test.java:7,发现死循环。

解决问题

对于CPU占用率飙高的场景,刚才列举的测试代码只是一种情况,还有如下可能。

1.业务线程出现大量阻塞,比如synchronized锁,可以检索状态为BLOCKED的线程找到堆栈信息,然后分析。

2.网络IO或者磁盘IO阻塞导致的,排查方法和上面一样。

3.GC线程频繁导致,线程的标识为GC task thread,对于这种情况需要专门分析,可能是年轻代设置不合理、大对象分配过多,old区存活对象过多。