当前位置: 首页 > news >正文

深入解析 JDK jstack 命令:线程分析的利器

你点赞了吗?你关注了吗?每天分享干货好文。

高并发解决方案与架构设计。

海量数据存储和性能优化。

通用框架/组件设计与封装。

如何设计合适的技术架构?

如何成功转型架构设计与技术管理?

在竞争激烈的大环境下,只有不断提升核心竞争力才能立于不败之地。

留言【我要晋级】,一对一指导,带你晋级。

引言

在 Java 应用开发中,线程问题(如死锁、CPU 占用过高、线程阻塞等)是常见的性能瓶颈和故障根源。JDK 工具 jstack 命令是分析和诊断 Java 线程状态的必备工具。

本文将从基础到实战,详细讲解 jstack 的使用方法、核心功能及常见问题的排查技巧,帮助你快速定位和解决线程相关问题。

一、jstack 是什么?

jstack 是 JDK 提供的一个命令行工具,用于生成 Java 虚拟机(JVM)的线程转储(Thread Dump)
线程转储是 JVM 中所有线程状态的快照,包含以下关键信息:

  • 线程的调用栈(Stack Trace)
  • 线程的状态(RUNNABLE、BLOCKED、WAITING 等)
  • 锁信息(持有的锁或等待的锁)
  • 线程优先级和所属线程组

通过分析线程转储,可以快速定位死锁、线程阻塞、CPU 占用过高等问题。

二、安装与基本使用

1. 环境要求

  • JDK 1.6+(建议使用与目标 Java 进程相同的 JDK 版本)
  • jstack 位于 JDK 的 bin 目录下(如 $JAVA_HOME/bin/jstack

2. 命令语法

jstack [options] <pid>          # 通过进程 ID 获取线程转储
jstack [options] <executable> <core>  # 分析核心转储文件
jstack [options] [server_id@]<remote server IP or hostname>  # 远程调试
常用参数:
  • -l:输出 详细的锁信息(如哪些线程持有锁,哪些在等待锁)
  • -F:强制生成线程转储(适用于进程无响应的情况)
  • -m:混合模式(显示 Java 和本地方法的堆栈帧)

三、实战:生成线程转储

1. 查找 Java 进程 ID

使用 jps 命令快速获取目标 Java 进程的 PID:

jps -l

2. 生成线程转储

jstack -l 12345 > thread_dump.txt  # 将输出重定向到文件

3. 强制生成转储(进程无响应时)

jstack -F -l 12345 > thread_dump_force.txt

四、线程转储分析指南

1. 线程状态解析

线程转储中的线程状态是分析问题的关键,常见状态如下:

  • NEW:刚创建的,还有调用 start() 之前的线程处在 NEW 状态上。此时线程与普通对象没区别,仅仅是堆中的一个线程对象而已。
  • RUNNABLE:正在运行,或者是具备运行条件,在运行队列中等待被 CPU 调度执行。如果线程长时间处在该状态下,说明线程运行时间长,或者一直没有CPU调度执行(线程饥饿现象)。
  • BLOCKED:线程在等待进入同步块或方法,本质就是在等待锁,例如被 synchronized 保护的代码块或方法。
  • WAITING:线程无限期等待其他线程的特定操作(如 Object.wait())。
  • TIMED_WAITING:线程在有限时间内等待(如 Thread.sleep(1000))。
  • TERMINATED:线程已终止,执行完 run 方法正常返回,或者抛出了运行时异常而结束,线程都会停留在这个状态。这个时候线程只剩下Thread对象了,没有什么用了。

2. 关键信息字段

"main" #1 prio=5 os_prio=0 tid=0x00007f8b4800e800 nid=0x1a03 waiting on condition [0x00007f8b50a0e000]java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for  <0x000000076b8a1c98> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)at com.example.MyService.doSomething(MyService.java:42)
  • 线程名称"main"
  • 线程 IDtid=0x00007f8b4800e800
  • 本地线程 ID(NID)nid=0x1a03(对应操作系统的线程 ID)。
  • 锁信息waiting for <0x000000076b8a1c98> 表示该线程正在等待某个锁。
  • prio:优先级,默认是 5

五、常见问题排查案例

常见现象:

CPU 使用率过高,响应慢

这种情况情况,需要在一次请求中进行多次 thread dump 文件的转储,做先后对比,如果 runnable 的线程前后差异大,说明系统属于正常情况,是服务器资源不够的问题,需要加大服务器资源。如果是在执行同一个方法,那就要去排查该方法的实现逻辑是否存在较大的问题,是否有优化的空间。

案例 1:死锁检测

死锁的主要表现是系统卡顿,无法响应用户的请求,进程的 CPU 占用率一般为零。

一般 JDK 5 以上,线程 Dump中可以直接报告出 Java 级别的死锁

步骤:

1、生成线程转储:jstack -l <pid>

2、搜索 deadlock 关键字:

Found one Java-level deadlock:
=============================
"Thread-1":waiting to lock monitor 0x00007f8b4800e800 (object 0x000000076b8a1c98, a java.lang.Object),which is held by "Thread-0"
"Thread-0":waiting to lock monitor 0x00007f8b4800e800 (object 0x000000076b8a1c98, a java.lang.Object),which is held by "Thread-1"

3、根据提示修复代码中的锁竞争逻辑。

案例 2:CPU 占用过高

该情况下,一般表现为系统响应慢。

步骤:

1、使用 top 命令,确定占用 CPU 最高的进程ID(PID)。

2、生成线程转储:jstack -l <pid>

3、使用 top -H -p pid 查看该进程中,各线程的CPU 占用情况。

4、上述看到的 PID 其实就是线程 ID,但是是十进制的,使用 printf "%x\n" PID,输出 十六进制。

5、在线程转储中搜索该 十六进制的线程,定位到问题线程的堆栈:

"WorkerThread" #42 prio=5 os_prio=0 tid=0x00007f8b4800e800 nid=0x3039 runnable [0x00007f8b50a0e000]java.lang.Thread.State: RUNNABLEat com.example.MyService.cpuIntensiveMethod(MyService.java:100)

6、分析代码逻辑,优化高 CPU 消耗的操作。

7、如果分析代码无法找到高 CPU 的理由时,就需要在一次请求中进行多次 thread dump 文件的转储,做先后对比,如果 runnable 的线程前后差异大,说明系统属于正常情况,是服务器资源不够的问题,需要加大服务器资源。如果是在执行同一个方法,那就说明该方法是有较大可能存在问题,还需要再去分析代码是否有优化的空间。

案例 3:线程长时间阻塞

步骤:
  1. 查找处于 BLOCKEDWAITING 状态的线程。
  2. 分析锁的持有者和等待链:
"DB-Connection-Thread" #5 prio=5 os_prio=0 tid=0x00007f8b4800e800 nid=0x1a03 waiting for monitor entry [0x00007f8b50a0e000]java.lang.Thread.State: BLOCKED (on object monitor)at com.example.DatabaseService.executeQuery(DatabaseService.java:50)- waiting to lock <0x000000076b8a1c98> (a com.example.ConnectionPool)
  1. 检查代码中是否存在不合理的锁竞争或资源争用。

六、高级技巧与工具

1. 自动化分析工具

  • VisualVM:图形化工具,支持实时线程监控和转储分析。
  • FastThread:在线线程转储分析工具(https://fastthread.io/)。
  • IBM Thread and Monitor Dump Analyzer:用于复杂线程问题的分析。

2. 结合其他命令

  • jps:快速查找 Java 进程 ID。
  • jstat:监控 JVM 统计信息(GC、类加载等)。
  • jmap:生成堆转储(Heap Dump)。

七、注意事项

  1. 权限问题:确保执行 jstack 的用户有权限访问目标 Java 进程。
  2. 生产环境谨慎使用 -F:强制生成转储可能导致 JVM 暂停。
  3. 多次采样:对于偶现问题,建议多次生成线程转储进行对比分析。

八、总结

jstack 是 Java 开发者必须掌握的诊断工具,能够快速定位线程相关的问题。通过本文的学习,你已经掌握了以下技能:

  1. 生成和分析线程转储。
  2. 诊断死锁、CPU 占用过高、线程阻塞等常见问题。
  3. 使用高级工具优化分析效率。

架构设计之道在于在不同的场景采用合适的架构设计,架构设计没有完美,只有合适。

在代码的路上,我们一起砥砺前行。用代码改变世界!

如果有其它问题,欢迎评论区沟通。

感谢观看,如果觉得对您有用,还请动动您那发财的手指头,点赞、转发、在看、收藏。

高并发解决方案与架构设计。

海量数据存储和性能优化。

通用框架/组件设计与封装。

如何设计合适的技术架构?

如何成功转型架构设计与技术管理?

在竞争激烈的大环境下,只有不断提升核心竞争力才能立于不败之地。

留言【我要晋级】,一对一指导,带你晋级。

相关文章:

  • CUDA编程中影响性能的小细节总结
  • Java PrintStream 类深度解析
  • 【AI提示词】经济学家
  • Pandas数据统计分析
  • 洛谷的几道题(2)
  • sed命令笔记250419
  • 金融数学专题6 证券问题与资本利得税
  • 5. 话题通信 ---- 发布方和订阅方python文件编写
  • EAGLE代码研读+模型复现
  • 缓慢前行,静待花开
  • three.js与webgl在buffer上的对应关系
  • 【Harmony】文本公共接口EditMenuOptions的使用
  • 第二十七讲:AI+农学导论
  • c++_csp-j算法 (2)
  • 使用vue2技术写了一个纯前端的静态网站商城-鲜花销售商城
  • 信息量、香农熵、交叉熵、KL散度总结
  • HSP代理Robocar POLI申请TRO冻结,涉及商标与版权
  • 深入剖析 HashMap:内部结构与性能优化
  • 6547网:2025年3月 Python编程等级考试一级真题试卷
  • 在RK3588上使用ZLMediaKit
  • 大尺度色情语聊、撮合卖淫嫖娼!一些交友软件暗藏“桃色陷阱”
  • 明查|俄罗斯征兵部门突袭澡堂抓捕壮丁?
  • 李家超:香港特区政府积极推进十五运会各项筹办工作
  • 市民建议公交广播增加“请勿大声喧哗”提示,上海交通委回复
  • 海南热带雨林国家公园核心保护区一水电站设施将拆除,曾被中央环保督察通报
  • 经济日报:从三个变化看外贸破局之道