dump常用命令及数据分析
1. ~*e !CLRStack -a
• 目的:查看所有托管线程的托管/本机调用栈,找出线程都卡在什么方法上(例如 .Result/.Wait()、长时间的 DB/网络调用、等待锁)。
2. !syncblk
• 目的:列出被锁(Monitor/lock)对象和等待线程,确认是否存在单点锁竞争或死锁。
3. !dumpheap -type System.Web.HttpContext (或 !dumpheap -type HttpContext)然后对若干 HttpContext 地址运行 !gcroot <addr>
• 目的:确认是否存在未释放的请求上下文导致请求积压或内存保留。
4. !dumpheap -type System.Threading.Tasks.Task 并对可疑 Task 做 !gcroot
• 目的:查找大量未完成或被阻塞的 Task/TaskCompletionSource 引用链(异步未完成导致泄漏/阻塞)。
5. !threadpool 或 !tp(若你的 SOS 版本支持)和 !runaway
• 目的:查看线程池队列/线程使用情况及各线程的 CPU 消耗。
6. 可选:~*k(所有线程本机栈)与 !analyze -v(如果尚未运行)。
~83s // 切换到线程 83(Thread ID:83)
!clrstack // 查看托管调用栈,定位具体业务代码(如命名空间、类名、方法名)
!dumpstack // 查看完整栈(含非托管),排除底层组件问题
!pe // 若存在异常,查看未处理异常信息
!syncblk命令结果数据分析
索引 同步块地址 锁持有/等待数 重入次数 锁持有者地址 系统线程ID 托管线程ID 锁对象地址 锁对象类型 ----------------------------------------------------------------------------------------------------------- 85 02cb632c 5 1 02c09760 509c 117 0e3fd18c System.Object 90 02cb6228 488 0 00000000 none none 213b68c4 System.Threading.TimerQueue 303 55cfbe50 43 1 400231e8 4758 237 0d3a85b8 System.Object ----------------------------- Total 317 // 总同步块数量 CCW 4 // COM 互用包装 RCW 5 // 运行时调用包装 ComClassFactory 0 Free 97 // 空闲同步块
二、每一列标准解析(官方定义)
表格
| 列名 | 含义 |
|---|---|
| Index | 同步块唯一编号 |
| SyncBlock | 同步块内存地址 |
| MonitorHeld | 锁总计数 = 持有数 + 等待数 |
| Recursion | 同一个线程重复加锁次数(重入) |
| Owning Thread | 持有锁的线程地址(0 = 无持有者) |
| Thread ID | 系统线程 ID(十六进制) |
| Managed ID | 托管线程 ID |
| SyncBlock Owner | 被加锁的对象(内存地址 + 类型) |
三、逐行极简解析
行 1:Index=85
- 锁对象:
System.Object(代码里的 lock 对象) - MonitorHeld=5:1 个线程持有锁,4 个线程在等待
- 持有者:线程 509c(托管 117)
- 状态:轻度锁竞争
行 2:Index=90 【最严重】
- 锁对象:.NET 内置定时器队列
TimerQueue - MonitorHeld=488:488 个线程 / 定时器在等待这个锁
- 持有者:none(无线程持有锁,但有大量等待)
- 结论:定时器严重堆积、线程池耗尽、Timer 完全阻塞
行 3:Index=303
- 锁对象:
System.Object - MonitorHeld=43:1 个线程持有,42 个线程等待
- 持有者:线程 4758(托管 237)
- 状态:重度锁竞争
四、总结(一句话)
- 无死锁
- Timer 队列堆积 488 次(高危)
- 两个业务锁存在严重线程争抢
- 程序表现:卡顿、响应慢、定时任务不执行、线程池耗尽
最终结论
你的程序不是死锁,而是:
- Timer 定时器严重阻塞 / 堆积
- 两处锁竞争严重
- 线程池资源耗尽

浙公网安备 33010602011771号