1. 计算密集型任务

一般线上的任务会分为:计算密集型任务IO密集型任务,其中计算密集型任务也叫做CPU密集型任务,是指CPU计算占主要的任务,CPU一直处于近乎满负荷状态。

在公司中,一般服务器CPU占用率超过一定的阈值,就会有报警,这时候我们就不得不去排查自己的线上任务在哪个地方导致了CPU占用过高。今天我们就介绍一下线上应用高CPU占用的排查。

2. 线上排查

我这里主要介绍 Java 应用的高 CPU 占用排查方法,由于一般服务器是没有图像化界面,无法用 JProfile 等去可视化排查,而用系统和 Java 命令排查是最轻量级的方法,所以我这里就只介绍用命令去排查的方法。下面以一段代码为例:

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * Created by gatsbynewton on 2017/2/21.
 */
public class JVM {

    public static void main(String[] args) {
        Executor executor = Executors.newFixedThreadPool(5);

        Task task1 = new Task();
        Task task2 = new Task();
        executor.execute(task1);
        executor.execute(task2);
    }

}

class Task implements Runnable{
    @Override
    public void run() {
        synchronized (Runnable.class){
            compute();
        }
    }

    private void compute(){
        int i = 0;
        while (true){
            i++;
        }
    }
}

示例代码比较简单,我起了两个线程一直做 i++的计算,这里不做赘述。

2.1 top 查看 CPU 状态

下面进入正题,我们要知道服务器 CPU、MEM 等的使用情况时,首先想到的命令莫过于 top

java 获取cpu占用率高的线程 java线上cpu占比过高怎么排查_堆栈


可以看到 PID=74293 的 Java 进程的 CPU 占用率高达 100%

2.2 ps 定位线程

找到高 CPU 占用的进程之后,我们还必须定位到具体是究竟是哪些线程导致了该进程的高 CPU 占用。刚好 ps 命令可以查看系统进程的状态的命令(PS:macOS 做不了实验,macOS 的 ps 命令查不到线程信息,除非要高级权限)。

java 获取cpu占用率高的线程 java线上cpu占比过高怎么排查_java 获取cpu占用率高的线程_02


这里的 TID 就是该进程对应的线程 ID。其中 -o 表示输出最终我们想要的信息。

2.3 jstack 查看堆栈信息

拿到 PID 和 TID 之后,我们就可以用 JDK 自带的 jstack 查看 Java 应用的堆栈信息,从而定位到具体哪一行代码导致高 CPU 占用。不过,堆栈信息中的线程 ID 是十六进制的,所以需要把之前的 TID 转成十六进制。

java 获取cpu占用率高的线程 java线上cpu占比过高怎么排查_java 获取cpu占用率高的线程_03


然后,我们就可以用 jstack 结合 grep 命令查看到指定线程的堆栈信息了,其中 -A 参数表示显示多少行。

java 获取cpu占用率高的线程 java线上cpu占比过高怎么排查_java 获取cpu占用率高的线程_04


可以看到 RUNNABLE 状态的线程正是我们要找的线程,根据堆栈信息可以看到是 JVM 类的 31 行代码有问题,而另一个线程目前处于 WAITING 状态。