jstack 工具

jstack(Stack Trace for Java) 命令用于生成当前时刻的线程快照(一般称为 threaddump 文件)。

线程快照就是当前虚拟机每条线程正在执行的方法堆栈集合,生成线程快照的目的通常是定位线程出现长时间停顿的原因,如线程间死锁,死循环、请求外部资源导致的长时间挂起等,都是导致线程长时间停顿的原因。

线程出现停顿是通过 jstack 来查看各个线层呢的调用堆栈,就可以获知没有响应的线程到底在后台做什么,或者等待什么资源。

jstack 命令格式:

jstack [option] vmid

下面我们就开始实践,说下环境:jdk 1.8, 操作系统 ubantu 20.04

排查 CPU 高占用问题

我们先写一个简单的死循环程序,来模拟 CPU 高占用问题。

测试代码如下:

public class MathTest {

    public int compute() {
        int a = 1026;
        int b = 2018;
        return (a + b) * 10;
    }

    public static void main(String[] args) {
        MathTest math = new MathTest();
        //System.out.println(math.compute());
        while (true) {
            math.compute();
        }
    }
}

编译和执行命令如下:

// 编译
javac MathTest.java

// 后台执行
java MathTest &

核心步骤

  1. jps 打印 Java 进程(查看是否启动)
zhengsh@zhengsh:/opt/apps$ jps
4541 MathTest
4559 Jps
  1. top 命令,查询指定进程的线程信息,然后通过 shift + m 排序
top -Hp 4541

结果如下:

image.png

找到高占用 cpu 的线程 id **4542**

  1. 通过 pid 转化为 16 进制
printf "%x\n" 4542
11be
  1. 查询所在的后面 30 行
jstack 4541|grep 11be -A 30

// 显示结果如下:
"main" #1 prio=5 os_prio=0 tid=0x00007f8efc00a800 nid=0x11be runnable [0x00007f8f016a3000]
   java.lang.Thread.State: RUNNABLE
    at MathTest.main(MathTest.java:13)

"VM Thread" os_prio=0 tid=0x00007f8efc074000 nid=0x11c1 runnable 

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f8efc01f800 nid=0x11bf runnable 

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f8efc021000 nid=0x11c0 runnable 

"VM Periodic Task Thread" os_prio=0 tid=0x00007f8efc0d9000 nid=0x11c8 waiting on condition 

JNI global references: 5

我们可以查询到 MathTest 类 13 行正在运行,在回看代码:

image.png

这里是有死循环调用,导致 CPU 过高。问题找到,解决完毕。

参考资料

  • 《深入理解 JVM 虚拟机 第四版》周志明