1. 功能简介
jstack
用于打印出给定的 Java进程ID
或远程调试服务的线程快照。线程快照是 Java 虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环造成的长时间等待。 通过 jstack
查看各个线程的调用堆栈,就可以知道停顿的线程到底在做什么,或者等待什么资源。 如果 Java 程序崩溃生成core文件,jstack
工具也可以获取 core 文件的 Java方法栈和本地方法栈的信息,从而可以直达事故现场分析出程序是如何崩溃的。另外需注意机器上当前登录用户和 Java 进程启动用户不是同一个时,无法使用该命令
- 需要注意的问题
不同的 JAVA 虚机的线程 DUMP 的创建方法和文件格式是不一样的,不同的 JVM 版本, dump信息也有差别。在实际运行中,往往一次 dump的信息,还不足以确认问题。建议产生三次 dump信息,如果每次 dump都指向同一个问题,才能确定问题的典型性
2. 使用方式
命令格式 | 参数说明 |
---|---|
jstack [ option ] pid | pid 为 Java 应用程序的进程号,连接到正在运行的进程 |
jstack [ option ] executable core | executable 为产生core dump的 java可执行程序,core为打印出的core文件,该命令可连接到核心文件 |
jstack [ option ] [ server-id@]remote-hostname-or-IP | remote-hostname-or-ip 是远程debug服务器的名称或IP,server-id是唯一id,假如一台主机上有多个远程debug服务会需求区分 |
Options | 功能 |
---|---|
-F | 当线程没有响应的时候强制打印栈信息 |
-m | 打印 Java 和 native c/c++ 框架的所有栈信息 |
-l | 长列表. 打印关于锁的附加信息,例如属于 java.util.concurrent 的 ownable synchronizers 列表 |
常见的使用方式如下
// 打印指定 Java 进程的栈信息
jstack -l 44295
// 输出指定 Java 进程的栈信息到 log 文件中
jstack -l 44295 > ~/44295.log
3. 分析注意
3.1 线程状态
通过 jstack
命令来分析线程的情况,首先要知道线程都有哪些状态,以下状态是查看线程堆栈信息时可能会看到的:
RUNNABLE
: 在虚拟机内执行的
BLOCKED
: 受阻塞并等待监视器锁,通常是多条线程争夺 synchronized 锁住资源失败触发
WATING
:无限期等待另一个线程执行特定操作
TIMED_WATING
:有时限的等待另一个线程的特定操作,通常由 sleep(n) 及定时任务触发
TERMINATED
:已退出的
3.2 线程信息的格式
参考 Java 对象锁的 Monitor 实现,可以知道一个 Monitor
在某个时刻,只能被一个线程拥有,拥有锁的线程就是 Active Thread
,而其它线程都是 Waiting Thread
,分别在两个队列 EntrySet
和 WaitSet
里面等待。在 EntrySet
中等待的线程状态是 Waiting for monitor entry
,表示等待获取锁;而在 WaitSet
中等待的线程状态是 in Object.wait()
,表示获得锁成功后又释放,等待被唤醒
一个典型线程信息如下,可以看到在第二行中标出的是线程状态 WAITING (on object monitor)
,表示在等待获取 Monitor
对象,而第一行的in Object.wait()
则是等待状态产生的原因,可以知道是线程当前执行条件不满足,自己调用了 wait()
方法放弃锁,等待被 notify()
。而waiting on <no object reference available>
则是当前线程对 Monitor
锁的操作状态,表示使用synchronized
申请对象锁成功后,释放锁并在WaitSet
里面等待被唤醒
“Disposer” #105 daemon prio=10 os_prio=31 cpu=35.51ms elapsed=241580.14s tid=0x00007fa74fb0f800 nid=0x20e87
in Object.wait()
[0x000070000a46c000]
java.lang.Thread.State:WAITING (on object monitor)
at java.lang.Object.wait([email protected]/Native Method)
-waiting on <no object reference available>
at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:155)
- waiting to re-lock in wait() <0x00000007493fc428> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:176)
at com.sun.webkit.Disposer.run([email protected]/Disposer.java:122)
at java.lang.Thread.run([email protected]/Thread.java:834)
.
Locked ownable synchronizers:
- None
线程对 Monitor 对象的操作状态 |
意义 |
---|---|
locked <地址> 目标 | 使用synchronized申请对象锁成功,监视器的拥有者 |
waiting to lock <地址> 目标 | 使用synchronized申请对象锁未成功,在EntrySet 队列等待 |
waiting on <地址> 目标 | 使用synchronized申请对象锁成功后,释放锁在WaitSet 区等待 |
parking to wait for <地址> 目标 | 等待获取锁时调用 park() 挂起线程 |
线程状态描述 | 线程状态 |
---|---|
runnable | 状态一般为RUNNABLE |
in Object.wait() | WaitSet 等待区等待,状态为 WAITING 或 TIMED_WAITING |
waiting for monitor entry | EntrySet 进入区等待,状态为 BLOCKED |
waiting on condition | WaitSet 等待区等待,线程被 park |
sleeping | 休眠的线程,调用了Thread.sleep() |
3.3 重点信息
搜索关键字 | 含义 |
---|---|
Deadlock | 死锁,重点关注 |
Waiting on condition | 等待资源,重点关注 |
Waiting on monitor entry | 等待获取监视器,重点关注 |
Blocked | 阻塞,重点关注 |
-
Deadlock
关键字
这个一般指多个线程调用时,自己占用了一部分资源,又需要请求对方占用的资源导致无限期等待对方释放资源的情况。这种情况堆栈写的会很明显,它会打印Found one Java-level deadlock
,然后指出造成死锁的线程的信息 -
Blocked
关键字
线程状态是Blocked
,说明线程等待资源超时。此时可查看其waiting to lock <0x0000000xxxxxxx>
信息,在 dump 日志里查找这个地址字符串,如发现有大量线程都在等待给这个地址上锁,可能在代码级别已经存在冲突的调用。此时如果能在日志里找到谁获得了这个锁(关键字locked <0x0000000xxxxxxx>
),则可以有效找到事故现场 -
Wait on condition
关键字
该状态表示等待资源,或等待某个条件的发生,具体原因需结合堆栈信息来分析。最常见的情况就是线程处于sleep
状态,等待被唤醒- 如果线程是用户线程,则证明该线程正在等待资源。一般是大量读取某资源,且该资源采用了资源锁的情况下,线程进入等待状态,等待资源的读取
- 如果发现有大量的线程都在处在
Wait on condition
,从线程 stack 看正等待网络读写,这可能是一个网络瓶颈的征兆,因为网络阻塞导致线程无法执行。一种情况是网络非常忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常到达
-
持续运行的IO
IO
操作可以在RUNNABLE
状态达成阻塞,例如数据库死锁、网络读写。 需要格外注意对IO
线程真实状态的分析,比如readLine()
造成的IO
阻塞,一般被捕捉到RUNNABLE
的IO
调用都是有问题的