诊断 Java 应用的过程中, 我们经常要去看线程的相关信息. 比如当前 JVM 有多少线程, 从启动到现在已经创建过多少线程, 线程的 CPU 使用率, 线程是不是发生了死锁, CPU 使用率过高是哪些代码导致的. 这要求我们对 Java 的线程必须有一定的了解, 才能比较有针对性的分析这些问题. 下面我们就从分析 Thread dump 开始介绍 Thread 相关的信息.
如何查看当前Java应用线程的信息
最快速的方式就是获得当前应用的 Thread dump, 然后使用文本的方式查看. 比如:
$ jps
49048 Jps
3499 Pycharm
$ jcmd 3499 Thread.print > /tmp/thread.txt
$ cat /tmp/thread.txt
复制代码
上面的例子中, 我们先是使用 jps
查看当前的用户的 Java 进程, 然后我们使用 jcmd <pid> Thread.print
命令来输出 Thread dump. 默认情况下, 这个命令会打印到当前控制台, 我们重定向到一个本地文件. 然后使用 cat
命令来查看具体的线程内容(如果你是桌面模式, 可以使用任何文本编辑器查看).
我们把整个 Thread dump 的信息, 分成3部分来分别讲解:
- 头部 JVM 及线程 Summary 部分
- 常规线程具体信息部分
- 尾部特殊线程及其它部分
Thread dump 头部 JVM 及线程ID列表
在最开始部分, 我们看到类似下面的信息:
3499:
2023-04-01 08:41:33
Full thread dump OpenJDK 64-Bit Server VM (17.0.5+1-b653.25 mixed mode):
Threads class SMR info:
_java_thread_list=0x000060000b029f00, length=113, elements={
0x00007fc18981e800, 0x00007fc18a01ee00, 0x00007fc18a01cc00, 0x00007fc18a8bea00,
... ...
0x00007fc1878f0200, 0x00007fc1410d6600, 0x00007fc18a489600, 0x00007fc131aefa00,
0x00007fc131ad4a00
}
复制代码
第一行是当前进程的进程号(pid), 第二行是产生 Thread dump的系统时间, 第三行主要是JDK的信息, 然后是所有线程的线程ID(tid)的列表,以及线程的数量(113). 上面SMR是 Safe Memory Reclamation 的缩写. 这部分信息通常情况下价值不大, 大部分有用的信息都在线程具体内容部分.
Thread dump 常规线程具体信息
接下来就是每个线程的具体元数据部分和线程栈部分. 我们拿其中一个例子来说明:
"pool-4-thread-1" #191 prio=5 os_prio=31 cpu=4012.76ms elapsed=313903.17s allocated=1229K defined_classes=23 tid=0x00007fc1898a8000 nid=0x23757 in Object.wait() [0x000070000a4d5000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(java.base@17.0.5/Native Method)
- waiting on <no object reference available>
at java.io.PipedInputStream.read(java.base@17.0.5/PipedInputStream.java:326)
- eliminated <0x0000000782139b00>