泽兴芝士网

一站式 IT 编程学习资源平台

WinDbg 分析 .NET Dump 线程锁问题

在定位 .NET 应用程序中的高 CPU 占用问题时,WinDbg 是非常强大的工具之一,尤其配合 SOS 扩展使用可以快速锁定“忙线程”或死锁等问题。

本文将基于一次实际的分析流程,演示如何一步步定位由线程锁引起的 CPU 高占用。

1. 加载 SOS 扩展(针对 .NET)

首先,我们需要加载 SOS.dll。根据你所调试的 .NET 版本不同,使用 .loadby 指令时的模块名也不同:

.loadby sos clr

注意:

  • .NET Framework 使用的是 clr.dll,所以 .loadby sos clr 正确;

  • 如果你调试的是 .NET Core.NET 5+,对应模块可能是 coreclr.dll

  • 可使用 lm 命令确认实际加载的模块名。

2.查看cpu占用高的线程

 !runaway

这个命令显示自 WinDbg 附加后各线程的 CPU 占用时间。

3. 查看每个线程的调用栈

查看所有线程的调用栈是分析的关键一步。我们使用以下命令:

~* k

这会列出所有线程的 原生调用堆栈(native stack)

关注以下三类线程特征:

持续执行的线程(高 CPU 嫌疑线程)

栈顶函数是业务逻辑方法、算法处理、循环等,说明该线程在“忙”,是最需要关注的对象。

卡在等待(阻塞)状态的线程

以下函数说明线程被阻塞,可能在等待锁或资源:

  • WaitForSingleObject

  • Monitor.Enter

  • WaitOne

  • Sleep

找到等待的资源后,看正在等待什么,如果正在等待GC,则继续找谁在GC

找到在执行 GC 的线程

如果调用栈中包含以下函数,说明线程正在 GC 中:

  • clr!GCHeap::GarbageCollect

  • clr!SVR::gc_heap::gc1

  • clr!SVR::gc_heap::gc2

  • clr!SVR::gc_heap::gc3

  • clr!GCHeap::GarbageCollectGeneration

  • clr!SVR::GCHeap::GarbageCollect

  • clr!GCHeap::gc_thread_function

  • GCInterface::Collect

频繁GC会挂起线程,增加CPU消耗。

4. 分析具体线程

在上一步中,如果你发现某个线程(例如线程 28)调用栈活跃、函数栈持续变化,或者涉及 GC、锁等待,可以使用以下命令聚焦:

 ~28s !clrstack

这将切换到线程 28 并显示它的托管调用栈,便于你进一步确认是否存在如下情况:

  • 死循环或密集计算导致高 CPU;

  • 一直等待某个锁对象,导致其他线程堆积;

  • 某些资源释放不及时,导致线程频繁争抢。

总结

通过上述方法,我们可以初步判断线程是否因锁或其他因素导致 CPU 占用异常。在实际排查中,掌握如下三点尤为重要:

  • 先宏观查看所有线程调用栈

  • 识别忙线程 / 等待线程/ GC线程

  • 进一步使用 !clrstack 分析托管调用栈

这是一种稳定、高效的诊断思路,尤其适用于高 CPU 的 dump 分析场景。

关注获取技术分享

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言