jstack 工具

jstack(Stack Trace for Java) 命令用于生成当前时刻的线程快照(一般称为 threaddump 文件)。
线程快照就是当前虚拟机每条线程正在执行的方法堆栈集合,生成线程快照的目的通常是定位线程出现长时间停顿的原因,如线程间死锁,死循环、请求外部资源导致的长时间挂起等,都是导致线程长时间停顿的原因。
线程出现停顿是通过 jstack 来查看各个线层呢的调用堆栈,就可以获知没有响应的线程到底在后台做什么,或者等待什么资源。
jstack 命令格式:

  1. jstack [option] vmid

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

排查 CPU 高占用问题

我们先写一个简单的死循环程序,来模拟 CPU 高占用问题。
测试代码如下:

  1. public class MathTest {
  2. public int compute() {
  3. int a = 1026;
  4. int b = 2018;
  5. return (a + b) * 10;
  6. }
  7. public static void main(String[] args) {
  8. MathTest math = new MathTest();
  9. //System.out.println(math.compute());
  10. while (true) {
  11. math.compute();
  12. }
  13. }
  14. }

编译和执行命令如下:

  1. // 编译
  2. javac MathTest.java
  3. // 后台执行
  4. java MathTest &

核心步骤

1. jps 打印 Java 进程(查看是否启动)

  1. zhengsh@zhengsh:/opt/apps$ jps
  2. 4541 MathTest
  3. 4559 Jps

2. top 命令,查询指定进程的线程信息,然后通过 shift + p 排序

  1. top -Hp 4541

结果如下:
JVM之CPU占用率过高排查思路 - 图1
找到高占用 cpu 的线程 id 4542

3. 通过 pid 转化为 16 进制

  1. printf "%x\n" 4542
  2. 11be

4. 查询所在的后面 30 行

  1. jstack 4541|grep 11be -A 30
  2. // 显示结果如下:
  3. "main" #1 prio=5 os_prio=0 tid=0x00007f8efc00a800 nid=0x11be runnable [0x00007f8f016a3000]
  4. java.lang.Thread.State: RUNNABLE
  5. at MathTest.main(MathTest.java:13)
  6. "VM Thread" os_prio=0 tid=0x00007f8efc074000 nid=0x11c1 runnable
  7. "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f8efc01f800 nid=0x11bf runnable
  8. "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f8efc021000 nid=0x11c0 runnable
  9. "VM Periodic Task Thread" os_prio=0 tid=0x00007f8efc0d9000 nid=0x11c8 waiting on condition
  10. JNI global references: 5

我们可以查询到 MathTest 类 13 行正在运行,在回看代码:
JVM之CPU占用率过高排查思路 - 图2
这里是有死循环调用,导致 CPU 过高。问题找到,解决完毕。

参考链接

https://juejin.cn/post/7086081411915989022