自写自查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过高可能是因为某些线程陷入死循环导致的,我们可以按照如下步骤排查:
- top:找到cpu占用率高的java进程id(pid)
- shift + h:找到进程下所有线程信息(线程id、线程所占用的cpu使用率)
- printf "%0x\n" <pid>:记住线程id并转换为16进制形式
- jstack <pid> | grep -A 10 <Thread 0x16 id>:利用java的jstack 查看java线程的信息
- cat -n file:查看找到该线程执行到的源码信息
- 解决问题
6、结束语
删除构造问题的镜像
[~]$ docker rmi java-test
制造问题过程中遇到其他命令和快捷键
- gg:定位到文件首部
- dG:清空光标位置到末尾的文件内容
查看正在运行的容器终端打印日志
[~]$ docker logs <容器id>