三步排查服务器中Java应用CPU飙高

服务器 0

文章目录

    • 启动一个Java应用模拟CPU飙高
    • top 命令查询CPU占用率高的Java应用
    • 分析该进程下占用CPU高的线程
    • 将线程PID转换为16进制
    • jstack 跟踪线程的调用栈
    • 查看源码

jstack 介绍
jstack 是 JDK 提供的一个命令行工具,用于生成 Java 进程的线程快照。
线程快照包含了 Java 进程中所有线程的状态信息,如线程的名称、线程的状态(RUNNABLE、WAITING、BLOCKED 等)以及线程的调用栈。
通过分析 jstack 生成的线程快照,可以帮助您诊断诸如死锁、线程阻塞、CPU 使用率过高等与线程相关的问题。

启动一个Java应用模拟CPU飙高

写了一个普通Java应用模拟CPU飙高,将它启动起来
在这里插入图片描述

top 命令查询CPU占用率高的Java应用

输入top 命令进入top命令界面后,按大写字母 P将根据CPU占用率排序, 按大写字母M将根据内存占用率排序

 top

查找CPU占用高的进程,复制进程PID 我这里是 461655
在这里插入图片描述

分析该进程下占用CPU高的线程

top -Hp {PID} 命令查看进程下的线程

 top -Hp 461656

查找CPU占用高的线程,复制线程程PID 我这里是 461656
在这里插入图片描述

将线程PID转换为16进制

使用 printf “0x%x” {PID} 命令转换16进制

 printf "0x%x" 461656

转为16进制后 0x70b58
在这里插入图片描述

jstack 跟踪线程的调用栈

jstack 是JDK提供的命令行工具,如果你配置了环境变量可以不用写全路径,没有环境变量就要加上你的JDK路径

 # jstack 进程PID  # |grep 0x70b58 过滤出该线程相关调用栈信息 # -A 50 输出后50行  /usr/local/jdk1.8.0_301/bin/jstack 461655 |grep 0x70b58 -A 50

以下输出调用栈中,可以看到在App.java中main方法第10行调用了线程就一直处于 java.lang.Thread.State: RUNNABLE 执行状态
在这里插入图片描述

查看源码

根据调用栈信息查看源码,原来代码中在计算圆周率后800万位。非常消耗CPU资源。

在这里插入图片描述

App.java 文件内容

package org.github.zuuyao;import java.math.BigDecimal;import java.math.RoundingMode;public class App {    public static void main(String[] args) {        System.out.println("calculatePi!");        // 计算圆周率精确到小数点八百万位        BigDecimal bigDecimal = calculatePi(8000000);        System.out.println("pi Value : " + bigDecimal.toString());    }    /**     * 模拟CPU飙高     */    public static void simulation() {        while (true) {            // 什么都不执行,一直死循环。占用大量CPU资源        }    }    /**     * 计算圆周率     *     * @param decimalPlaces 圆周率小数点位数     * @return 计算结果     */    public static BigDecimal calculatePi(int decimalPlaces) {        BigDecimal pi = BigDecimal.ZERO;        BigDecimal sixteen = BigDecimal.valueOf(16);        BigDecimal one = BigDecimal.ONE;        for (int k = 0; k <= decimalPlaces; k++) {            BigDecimal kBig = BigDecimal.valueOf(k);            BigDecimal term = one.divide(sixteen.pow(k), decimalPlaces + 10, RoundingMode.HALF_UP);            term = term.multiply(                BigDecimal.valueOf(4)                    .divide(BigDecimal.valueOf(8 * k + 1), decimalPlaces + 10, RoundingMode.HALF_UP)                    .subtract(BigDecimal.valueOf(2)                        .divide(BigDecimal.valueOf(8 * k + 4), decimalPlaces + 10,                            RoundingMode.HALF_UP))                    .subtract(BigDecimal.valueOf(1)                        .divide(BigDecimal.valueOf(8 * k + 5), decimalPlaces + 10,                            RoundingMode.HALF_UP))                    .subtract(BigDecimal.valueOf(1)                        .divide(BigDecimal.valueOf(8 * k + 6), decimalPlaces + 10,                            RoundingMode.HALF_UP))            );            pi = pi.add(term);        }        return pi.setScale(decimalPlaces, RoundingMode.HALF_UP);    }}

pom.xml 文件内容

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>  <groupId>org.github.zuuyao</groupId>  <artifactId>troubleshooting-demo</artifactId>  <version>1.0-SNAPSHOT</version>  <packaging>jar</packaging>  <name>troubleshooting-demo</name>  <url>http://maven.apache.org</url>  <properties>    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  </properties>  <dependencies>  </dependencies>  <build>    <finalName>${artifactId}</finalName>    <plugins>      <plugin>        <groupId>org.apache.maven.plugins</groupId>        <artifactId>maven-jar-plugin</artifactId>        <configuration>          <archive>            <manifest>              <addClasspath>true</addClasspath>              <!--主启动类-->              <mainClass>org.github.zuuyao.App</mainClass>            </manifest>          </archive>        </configuration>      </plugin>    </plugins>  </build></project>

也许您对下面的内容还感兴趣: