WinDbg分析.NET死锁:!threads显持有锁但!syncblk等无结果
排查.NET ThinLock死锁的具体步骤
针对你遇到的ThinLock无法通过常规命令排查的情况,以下是具体分析手段:
定位线程栈中的锁对象
- 用
!threads找到疑似持有锁的线程,记录其托管ID(比如ID: 3),切换到该线程:~3s - 执行
!clrstack -l查看带局部变量的调用栈,找到Monitor.Enter或lock语句对应的代码位置,定位栈上可能被锁定的对象;或者直接用!dso列出线程栈上的所有托管对象,筛选出可能作为锁的对象(比如业务对象、自定义类型实例)。
- 用
解析ThinLock的对象头信息
ThinLock存储在对象的头部(未升级为重量级锁时不会出现在syncblk列表),可以通过以下方式查看:- 对
!dumpheap -thinlock输出的对象地址,执行!do -d [对象地址]查看原始对象头数据(-d参数显示原始内存):- 64位环境下,对象头前8字节为锁信息:低32位是持有锁的OS线程ID,高16位是递归计数,剩余位为标志位;
- 32位环境下,对象头前4字节存储ThinLock信息,格式为线程ID(低24位)+ 递归计数(高8位)。
- 使用SOSEX的
!mx [对象地址]命令,直接查看对象的锁状态,包括ThinLock的持有线程、递归次数,比手动解析更直观。
- 对
结合Finalizer线程状态分析
- 执行
!finalizequeue查看终结器队列中的对象,检查这些对象是否在!dumpheap -thinlock的结果中——如果终结器线程在处理某个持有ThinLock的对象,可能和其他线程形成死锁。 - 切换到Finalizer线程(通常托管ID为
0),执行!clrstack和!dso,查看它正在处理的对象,以及是否等待某个锁。
- 执行
.NET Core/.NET 5+ 额外排查手段
如果是较新版本的.NET,可尝试:- 执行
!lockstat dump命令(需确保SOS版本匹配),该命令能展示轻量级锁、自旋锁的持有和等待信息; - 使用
dotnet-dump analyze结合syncblk -all(虽不显示ThinLock,但可确认是否有升级后的重量级锁),补充排查维度。
- 执行
内容的提问来源于stack exchange,提问作者Stijn
相关产品推荐
相关产品推荐

