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

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>

  • 27
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZuuuuYao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值