线上环境突然卡死,没有可视化工具,只能拿到 jstack 日志,面对几万行的文本,确实让人头大。别慌,这里提供一套系统性的方法,帮你从 jstack 日志中揪出死锁线程。
第一步:拿到 jstack 日志
这个不用多说,使用 jstack pid > jstack.log 命令拿到日志文件。
第二步:初步筛选,关注关键信息
用文本编辑器打开 jstack.log 文件,直接搜索以下关键字,快速定位问题:
- deadlock:这是最直接的死锁提示,jstack 会自动检测死锁并打印相关信息。
- waiting to lock:线程等待锁的标志,关注哪些线程在等待锁。
- 持有锁的线程 (owned by):找到持有锁的线程,看看它在做什么。
- BLOCKED:线程阻塞状态,长时间处于阻塞状态的线程可能存在问题。
第三步:分析死锁信息(如果找到 deadlock 关键字)
如果 jstack 输出了死锁信息,通常会包含以下内容:
- Found one Java-level deadlock: 表示发现了一个 Java 级别的死锁。
- 线程信息: 哪些线程参与了死锁,以及它们正在等待的锁。
- 锁信息: 哪个线程持有了哪些锁,导致其他线程无法继续执行。
仔细阅读这些信息,就能直接定位到死锁的线程和锁。例如:
Found one Java-level deadlock:
=============================
"thread-1":
waiting to lock monitor 0x00007f... (a java.lang.Object)
which is held by "thread-2"
"thread-2":
waiting to lock monitor 0x00007f... (a java.lang.Object)
which is held by "thread-1"
这个例子中,thread-1 和 thread-2 互相等待对方持有的锁,导致死锁。
第四步:如果没有死锁信息,手动分析线程状态
如果 jstack 没有直接提示死锁,就需要手动分析线程状态。
- 查找 BLOCKED 状态的线程: 筛选出所有状态为
BLOCKED的线程。 - 分析线程堆栈: 查看这些线程的堆栈信息,找到它们正在等待的锁。
- 找到持有锁的线程: 根据锁的信息,找到持有这些锁的线程。
- 分析锁的竞争情况: 如果多个线程都在竞争同一个锁,并且其中一个线程长时间持有该锁,就可能导致死锁或性能问题。
第五步:重点关注业务线程
在分析 jstack 日志时,要重点关注业务线程,通常线程名会包含业务相关的关键字,例如订单、支付、用户等。 忽略 JVM 自身线程 (如 GC 线程)。
第六步:借助工具提高效率
虽然没有可视化工具,但可以使用一些文本处理工具来提高效率,例如:
- grep: 用于快速搜索关键字。
- awk/sed: 用于提取和格式化文本。
- 文本编辑器: 使用文本编辑器的查找、替换、高亮等功能,方便分析日志。
总结:
分析 jstack 日志需要耐心和细心, 掌握以上方法,就能快速定位死锁线程,解决线上问题。记住,关键在于理解线程状态、锁的竞争情况,以及业务代码的逻辑。