Jstack 概述

Jstack 工具是 Java 虚拟机自带的一种堆栈跟踪工具。Jstack 用于生成或导出(DUMP)JVM 虚拟机运行实例当前时刻的线程快照。线程快照是当前JVM实例内每一个线程正在执行的方法堆栈的集合,生成或导出线程快照的主要目的是定位线程出现长时间运行、停顿或者阻塞的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。线程出现停顿的时候通过 Jstack 来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。

Jstack 命令的语法格式如下:

  1. jstack <pid> // pid 表示 Java 进程 id,可以使用 jsp 命令查看

一般情况下,通过 Jstack 输出的线程信息主要包括:JVM 线程、用户线程等。其中,JVM 线程在 JVM 启动时就存在,主要用于执行譬如垃圾回收、低内存的检测等后台任务,这些线程往往在 JVM 初始化的时候就存在。而用户线程则是在程序创建了新的线程时才会生成。这里需要注意的是:

  1. 在实际运行中,往往一次 DUMP 的信息不足以确认问题。建议产生三次 DUMP 信息,如果每次 DUMP 都指向同一个问题,我们才能确定问题的典型性
  2. 不同的 Java 虚拟机的线程导出来的 DUMP 信息格式是不一样的,并且同一个 JVM 的不同版本,DUMP 信息也有差别

Jstack 输出的信息

使用 Jstack 获取线程状态信息时,获取到的数据如下所示:
image.png

  • GC task thread 为垃圾回收线程,此类线程会负责进行垃圾回收。通常 JVM 会启动多个 GC 线程,在 GC 线程的名称中,# 后面的数字会累加,如 GC taskthread#1、GC task thread#2 等
  • VM Periodic Task Thread 线程是 JVM 周期性任务调度的线程,该线程在 JVM 内使用得比较频繁,比如定期的内存监控、JVM 运行状况监控
  • tid:线程实例在 JVM 进程中的 id
  • nid:线程实例在操作系统中对应的底层线程的线程 id
  • prio:线程实例在 JVM 进程中的优先级
  • os_prio:线程实例在操作系统中对应的底层线程的优先级
  • 线程状态:如 runnable、waiting on condition 等

Jstack 查看线程 sleep 状态

先看下面的示例:

  1. public class SleepTest {
  2. // 睡眠时长:5s
  3. public static final int SLEEP_GAP = 5000;
  4. // 睡眠次数,值大一点方便使用 Jstack
  5. public static final int MAX_TURN = 50;
  6. public static void main(String[] args) {
  7. for (int i = 0; i < 5; i++) {
  8. Thread thread = new SleepThread();
  9. thread.start();
  10. }
  11. System.out.println(Thread.currentThread() + " 运行结束");
  12. }
  13. static class SleepThread extends Thread {
  14. static int threadSeqNumber = 1;
  15. public SleepThread() {
  16. super("SleepThread-" + threadSeqNumber);
  17. threadSeqNumber++;
  18. }
  19. @Override
  20. public void run() {
  21. try {
  22. for (int i = 0; i < MAX_TURN; i++) {
  23. System.out.println(getName() + ",睡眠次数:" + i);
  24. Thread.sleep(SLEEP_GAP);
  25. }
  26. } catch (Exception e) {
  27. System.out.println(getName() + " 发生异常中断");
  28. }
  29. System.out.println(getName() + " 运行结束");
  30. }
  31. }
  32. }

在代码运行之后,打开终端,键入 jps 命令查看进程信息,其中, SleepTest 进程 的 pid38376

  1. > jps
  2. 34880 Launcher
  3. 22036 DeadLock
  4. 40452 Jps
  5. 38376 SleepTest
  6. 17180

通过 jstack 命令查看进程信息:

  1. > jstack 38376
  2. 2021-07-12 23:13:21
  3. Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.172-b11 mixed mode):
  4. "DestroyJavaVM" #16 prio=5 os_prio=0 tid=0x0000000002af3000 nid=0x6e5c waiting on condition [0x0000000000000000]
  5. java.lang.Thread.State: RUNNABLE
  6. "SleepThread-5" #15 prio=5 os_prio=0 tid=0x0000000018b71000 nid=0x9430 waiting on condition [0x0000000019b6f000]
  7. java.lang.Thread.State: TIMED_WAITING (sleeping)
  8. at java.lang.Thread.sleep(Native Method)
  9. at com.bujian.concurrence.SleepTest$SleepThread.run(SleepTest.java:34)
  10. "SleepThread-4" #14 prio=5 os_prio=0 tid=0x0000000018b70800 nid=0x9a00 waiting on condition [0x0000000019a6f000]
  11. java.lang.Thread.State: TIMED_WAITING (sleeping)
  12. at java.lang.Thread.sleep(Native Method)
  13. at com.bujian.concurrence.SleepTest$SleepThread.run(SleepTest.java:34)
  14. "SleepThread-3" #13 prio=5 os_prio=0 tid=0x0000000018b6d800 nid=0x9cfc waiting on condition [0x000000001996f000]
  15. java.lang.Thread.State: TIMED_WAITING (sleeping)
  16. at java.lang.Thread.sleep(Native Method)
  17. at com.bujian.concurrence.SleepTest$SleepThread.run(SleepTest.java:34)
  18. "SleepThread-2" #12 prio=5 os_prio=0 tid=0x0000000018b6b000 nid=0x4248 waiting on condition [0x000000001986f000]
  19. java.lang.Thread.State: TIMED_WAITING (sleeping)
  20. at java.lang.Thread.sleep(Native Method)
  21. at com.bujian.concurrence.SleepTest$SleepThread.run(SleepTest.java:34)
  22. "SleepThread-1" #11 prio=5 os_prio=0 tid=0x0000000018b6a000 nid=0x9318 waiting on condition [0x000000001976f000]
  23. java.lang.Thread.State: TIMED_WAITING (sleeping)
  24. at java.lang.Thread.sleep(Native Method)
  25. at com.bujian.concurrence.SleepTest$SleepThread.run(SleepTest.java:34)
  26. ......
  27. "VM Thread" os_prio=2 tid=0x00000000176d7800 nid=0x9e0c runnable
  28. "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002b08800 nid=0xa6c8 runnable
  29. "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002b0a800 nid=0xa0a4 runnable
  30. "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002b0c000 nid=0x9b4c runnable
  31. "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002b0d800 nid=0x4cd8 runnable
  32. "VM Periodic Task Thread" os_prio=2 tid=0x0000000018b5d000 nid=0x753c waiting on condition
  33. JNI global references: 12

通过以上的 Jstack 指令输出,可以看到在进行线程 DUMP 的时间点,所创建的 5 个 SleepThread 线程都处于 TIMED_WAITING(sleeping) 状态。当线程睡眠时间满后,线程不一定会立即得到执行,因为此时 CPU 可能正在执行其他的任务,线程首先进入就绪状态,等待分配 CPU 时间片以便有机会执行。