1. 生成 jstack 日志
-
命令:
jstack <pid> > thread_dump.txt # 直接生成线程转储 # 或使用更现代的 jcmd 工具: jcmd <pid> Thread.print > thread_dump.txt<pid>是 Java 进程的 ID,可通过jps或ps -ef | grep java查看。 -
建议:在问题发生时多次生成日志(例如间隔 5-10 秒),便于对比分析。
2. 理解线程转储结构
一个典型的线程转储包含以下信息:
- 线程状态:如
RUNNABLE、BLOCKED、WAITING、TIMED_WAITING。 - 线程栈轨迹:显示线程当前执行的方法调用链。
- 锁信息:线程持有的锁或等待的锁(如
0x000000076bf0f7d8)。 - 守护线程/用户线程:标识线程类型。
3. 关键分析步骤
(1) 定位死锁
-
搜索关键字:在日志中查找
deadlock或Found one Java-level deadlock。 -
查看死锁详情:死锁的线程会明确标识,并显示相互等待的锁资源。
-
示例:
"Thread-1" #12 prio=5 os_prio=0 tid=0x00007f01340cc800 waiting for monitor entry [0x00007f00f1efd000] java.lang.Thread.State: BLOCKED (on object monitor) at com.example.DeadlockExample.methodB(DeadlockExample.java:25) - waiting to lock <0x000000076bf0f7d8> (a java.lang.Object) - locked <0x000000076bf0f7e8> (a java.lang.Object)
(2) 分析高 CPU 占用
-
步骤:
- 使用
top -H -p <pid>或pidstat -t -p <pid>查找占用 CPU 高的线程 ID。 - 将线程 ID 转换为十六进制(如线程 ID
12345→0x3039)。 - 在
jstack日志中搜索该十六进制 ID(nid=0x3039),查看线程的栈轨迹。
- 使用
-
常见原因:
- 循环未退出(如
while(true)无休眠)。 - 密集计算(如复杂算法、正则表达式)。
- 锁竞争激烈(大量线程处于
BLOCKED状态)。
- 循环未退出(如
(3) 检查线程阻塞(WAITING/BLOCKED)
-
常见场景:
WAITING (on object monitor):线程在等待wait()或notify()。BLOCKED (on object monitor):线程在等待进入同步块或获取锁。TIMED_WAITING (sleeping):线程调用了sleep()或join(timeout)。
-
关注点:
- 大量线程处于
BLOCKED状态可能表明锁竞争。 - 长时间
WAITING可能是资源未释放或任务调度问题。
- 大量线程处于
(4) 识别资源等待
-
I/O 或网络操作:线程可能在等待数据库、HTTP 响应或文件读写。
"http-nio-8080-exec-1" #20 daemon prio=5 os_prio=0 tid=0x00007f01340cc800 runnable [0x00007f00f1efd000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)- 如果大量线程卡在
socketRead,可能是下游服务响应慢。
- 如果大量线程卡在
(5) 检查线程池问题
-
线程池中的线程若长期空闲或任务堆积,可能表明任务分配不均或队列过长。
-
示例:
"pool-1-thread-1" #15 prio=5 os_prio=0 tid=0x00007f01340cc800 waiting on condition [0x00007f00f1efd000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076bf0f7d8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
4. 工具辅助分析
- VisualVM:可视化分析线程状态和锁竞争。
- FastThread(在线工具):上传
jstack日志自动生成分析报告,链接。 - IDEA 插件:如 jstack-analyzer,提供代码级关联。
- grep/sed/awk:通过命令行快速筛选关键信息(如
grep "BLOCKED" thread_dump.txt)。
5. 常见问题模式
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
大量 BLOCKED 线程 | 锁竞争激烈 | 优化锁粒度或使用无锁数据结构 |
线程长期 WAITING | 资源未释放或任务饥饿 | 检查 notify()/notifyAll() 调用 |
高 CPU 线程处于 RUNNABLE | 死循环或密集计算 | 优化算法或增加休眠 |
| 线程卡在 I/O 操作 | 外部服务响应慢或网络问题 | 优化下游服务或设置超时 |
6. 注意事项
- 单次
jstack日志可能不足以定位问题,需结合多次日志和监控工具(如jstat、jmap)。 - 线程状态是瞬时的,需结合系统监控(如 CPU、内存、磁盘 I/O)综合分析。
- 生产环境中建议使用 APM 工具(如 Arthas、SkyWalking)实时监控线程状态。
通过以上步骤,可以系统性地分析 jstack 日志,定位性能瓶颈或死锁问题。