1. 程式人生 > 其它 >自寫自查CPU過高問題

自寫自查CPU過高問題

自寫自查CPU過高問題

目錄

1、問題

說一說線上CPU過高怎麼辦?

2、製造問題(通過docker方式)

2.1、準備檔案內容

建立目錄

[~]$ cd /
[~]$ mkdir /app
[~]$ cd ~
[~]$ mkdir cpuTestDir
[~]$ vi dockerfile

輸入快捷鍵 i 指令,編寫dockerfile檔案

檔案內容如下:

# 基於java 9
FROM java:9
# 設定工作目錄
WORKDIR /app
# 複製檔案到工作目錄
COPY . /app
# 設定java環境變數
ENV PATH=$PATH:$JAVA_HOME/bin
ENV JRE_HOME=${JAVA_HOME}/jre
ENV CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
# 編譯
RUN ["/usr/lib/jvm/java-9-openjdk-amd64/bin/javac","CPUTest.java"]
# 執行
ENTRYPOINT ["/usr/lib/jvm/java-9-openjdk-amd64/bin/java","CPUTest"]

執行快捷指令(英文模式下輸入):

  • esc
  • shift + :
  • wq

按照上述方法,在當前目錄建立CPUTest.java檔案,檔案內容如下:

import java.math.BigInteger;

/**
 * @author lishanbiao
 * @date 2022/5/27 7:27 上午
 */
public class CPUTest {
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            BigInteger i = new BigInteger("1");
            BigInteger j = new BigInteger("1");
            while (true) {
                i.add(j);
            }
        }).start();
        Thread.sleep(100000000);
    }
}

執行命令檢視:

[~]$ ls
CPUTest.java  dockerfile

2.2、構建映象

[~]$ docker build . -f dockerfile -t java-test:latest

檢視映象資訊

[~]$ docker images
REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
java-test        latest    b44aa1a44e4d   17 seconds ago   579MB

執行映象

[~]$ docker run -itd java-test:latest
Options Mean
-i 以互動模式執行容器,通常與 -t 同時使用;
-t 為容器重新分配一個偽輸入終端,通常與 -i 同時使用;
-d 後臺執行容器,並返回容器ID;

檢視執行中的容器

[~]$ docker ps

進入執行中的容器

[~]$ docker  exec -it 728b98b3b581 /bin/bash
root@728b98b3b581:/app#

執行java程式

root@728b98b3b581:/app# ls
CPUTest.class  CPUTest.java  dockerfile
root@728b98b3b581:/app# java CPUTest
執行成功……

使用快捷鍵:ctrl + c 中斷終端(此時java程式已經在執行)

3、排查問題

root@728b98b3b581:/app# top

找到cpu佔用率高的程序id(pid),然後執行q快捷鍵指令退出,之後執行指令

root@728b98b3b581:/app# top -p 1

Shift + h使用快捷鍵顯示程序下所有執行緒

此時cpu最高的pid就是執行緒所對應的執行緒id(tid,spid,lwf)

轉換為16進位制

root@728b98b3b581:/app# printf "%0x\n" 17
11

此時利用java自帶的檢視執行緒命令檢視該執行緒的執行狀況:jstack <pid> | grep -A 10 <Thread 0x16 id>

root@728b98b3b581:/app# jstack 1 | grep -A 10 11

此時我們可以找到問題所在:CPUTest.java:13

java.lang.Thread.State: RUNNABLE
        at CPUTest.lambda$main$0(CPUTest.java:13)
        at CPUTest$$Lambda$1/1018547642.run(Unknown Source)
        at java.lang.Thread.run(java.base@9-Debian/Thread.java:844)

4、定位問題

這個時候,為了找到java對應的執行緒執行原始碼,我們先退出容器

root@728b98b3b581:/app# exit
exit
[root@node1 ~]#

定位原始碼

[root@node1 ~]# cd cpuProblemByDocker/
[root@node1 cpuProblemByDocker]# ls
CPUTest.java  dockerfile
[root@node1 cpuProblemByDocker]# cat -n CPUTest.java 
     1  import java.math.BigInteger;
     2
     3  /**
     4   * @author lishanbiao
     5   * @date 2022/5/27 7:27 上午
     6   */
     7  public class CPUTest {
     8      public static void main(String[] args) throws InterruptedException {
     9          new Thread(() -> {
    10              BigInteger i = new BigInteger("1");
    11              BigInteger j = new BigInteger("1");
    12              while (true) {
    13                  i.add(j);
    14              }
    15          }).start();
    16          System.out.println("執行成功……");
    17          Thread.sleep(100000000);
    18      }
    19  }
    20
[root@node1 cpuProblemByDocker]# 

我們最終發現了存在問題的第13行,原來是一個死迴圈!

5、解決問題

如果這是真實發生的問題,請及時解決,我這裡是構造的問題,所以不用那麼麻煩啦~

6、回答問題

線上CPU過高可能是因為某些執行緒陷入死迴圈導致的,我們可以按照如下步驟排查:

  1. top:找到cpu佔用率高的java程序id(pid)
  2. shift + h:找到程序下所有執行緒資訊(執行緒id、執行緒所佔用的cpu使用率)
  3. printf "%0x\n" <pid>:記住執行緒id並轉換為16進位制形式
  4. jstack <pid> | grep -A 10 <Thread 0x16 id>:利用java的jstack 檢視java執行緒的資訊
  5. cat -n file:檢視找到該執行緒執行到的原始碼資訊
  6. 解決問題

6、結束語

刪除構造問題的映象

[~]$ docker rmi java-test

製造問題過程中遇到其他命令和快捷鍵

  • gg:定位到檔案首部
  • dG:清空游標位置到末尾的檔案內容

檢視正在執行的容器終端列印日誌

[~]$ docker logs <容器id>