0

0

c# WinDbg 和 PerfView 在高并发问题排查中的应用

煙雲

煙雲

发布时间:2026-01-01 08:05:17

|

143人浏览过

|

来源于php中文网

原创

WinDbg与PerfView联动可高效定位高并发响应慢根因:先用!threads和!syncblk查托管线程阻塞与锁竞争,再用PerfView采样CPU/GC热点及ThreadPool队列,结合!dumpheap和~*k分析托管/非托管混合瓶颈,需交叉验证三者数据。

c# windbg 和 perfview 在高并发问题排查中的应用

WinDbg 查看线程阻塞和锁竞争

高并发下响应变慢,第一反应是线程卡在同步原语上。用 WinDbg 加载 dump 后,!threads 能快速列出所有托管线程状态,重点关注 State 列为 Wait:Preemptive 但实际在等锁的线程。

接着用 !syncblk 查看同步块持有情况,输出里 MonitorHeld 非 0 的条目说明有线程正持有锁;再配合 !dlk(需先加载 SOS.dll)可自动识别死锁链——但要注意:它只检测 CLR 层面的 Monitor.Enter / lock,对 SpinLockReaderWriterLockSlim 或 native mutex 不敏感。

  • ~*e !clrstack 查每个线程的托管调用,确认是否卡在 Monitor.WaitWaitHandle.WaitOneTask.Wait
  • 若看到大量线程停在 System.Threading.Monitor.ObjWait,大概率是某个共享资源被单一线程长期独占
  • 注意 !threads -state 中的 Background 线程可能也在争抢同一把锁,不能只盯主线程

PerfView 抓取高并发下的 CPU 和 GC 热点

WinDbg 擅长“静态快照”,PerfView 更适合“动态采样”。启动时勾选 CPU Stack + GC Heap Alloc,采样时间建议 ≥30 秒,避免噪声干扰。导出 Events 视图后,重点看两个维度:

CPU 热点:展开 Microsoft-Windows-DotNETRuntime/MethodJITVerbose 或直接看 Stacks 页的 Hot Path,高频出现 ConcurrentDictionary`2.TryGetValueStringBuilder.Append 不一定错,但若伴随大量 GC/Start 事件,则可能是分配压力引发的间接竞争。

GC 压力信号:观察 GC/End 事件间隔是否小于 1 秒,且 Gen 2 次数突增——这常意味着大对象堆(LOH)频繁分配,而 LOH 分配在高并发下会触发全局锁 gc_heap::allocate_large,成为隐形瓶颈。

AmEav WebSite 企业网站管理系统1.0
AmEav WebSite 企业网站管理系统1.0

系统功能强大、操作便捷并具有高度延续开发的内容与知识管理系统,并可集合系统强大的新闻、产品、下载、投票、人才、留言、在线订购、搜索引擎优化、等功能模块,为企业部门提供一个简单、易用、开放、可扩展的企业信息门户平台或电子商务运行平台。开发人员为脆弱页面专门设计了防刷新系统,自动阻止恶意访问和攻击;安全检查应用于每一处代码中,每个提交到系统查询语句中的变量都经过过滤,可自动屏蔽恶意攻击代码,从而全面防

下载
  • PerfView 默认不捕获 ThreadPool 队列长度,需手动开启 Microsoft-Windows-DotNETRuntime/ThreadPool/WorkerThreadAdjustment 事件
  • 对比 Alloc By TypeAlloc By Stack,能定位是哪个业务逻辑路径在疯狂 new 对象
  • 导出 GCStats 表格时,注意 PauseMSec 列总和占比,若 >15%,说明 GC 已显著拖慢吞吐

WinDbg + PerfView 联动定位 async/await 隐形阻塞

async 方法没用 await 但写了 async 修饰符,或在 ConfigureAwait(false) 缺失场景下调度回 UI/ASP.NET 上下文,都会导致线程池饥饿。PerfView 中若发现 ThreadPool/WorkerThreadAdjustment 频繁触发扩容,同时 ThreadPool/QueuedWorkItem 队列深度持续 >100,就要怀疑 await 后续执行被卡住。

此时切回 WinDbg,用 !dumpheap -type System.Threading.Tasks.Task 查看未完成的 Task 数量;再挑几个状态为 WaitingForActivation 的 Task,用 !dumpobj

看其 m_stateFlagsm_continuation 字段——如果 m_continuationAsyncMethodBuilderCore 实例,且其 m_stateMachine 的字段显示 awaiter 仍处于 IsCompleted == false,基本确认是 I/O 或定时器未触发回调。

  • 检查是否误用 Task.ResultTask.Wait() 在 ASP.NET Core 同步上下文中,这会直接阻塞整个请求线程
  • PerfView 的 Stacks 页中搜索 TaskAwaiter.HandleNonSuccessAndDebuggerNotification,它的调用频次异常高,往往对应 await 后续逻辑执行缓慢
  • WinDbg 里 !dumpheap -stat 若显示大量 System.Object[]System.Byte[],可能是 JSON 序列化/反序列化过程中 buffer 复用失败,引发额外分配和锁争用

容易被忽略的托管与非托管混合瓶颈

高并发服务常调用 native DLL(如加密、图像处理),这类调用不会出现在 !clrstack 中,但会卡住线程。WinDbg 里用 ~*k 看所有线程的原生栈,若某线程栈顶是 ntdll!NtWaitForSingleObjectkernel32!WaitForMultipleObjects,且没有对应的托管帧,就得怀疑 native 层阻塞。

PerfView 可通过开启 Windows Kernel/Process Thread 事件并勾选 SampleProfile,把 native 栈也纳入采样范围。此时 Stacks 页会出现 ntdll!RtlEnterCriticalSectionmsvcr120!malloc 这类符号——前者说明 native 代码用了临界区且持有时间过长,后者则暗示频繁 malloc/free 引发的 heap lock 竞争。

  • 托管代码调用 native 函数时,务必检查 P/Invoke 声明是否加了 [DllImport(..., CallingConvention = CallingConvention.Cdecl)],否则调用约定不匹配会导致栈损坏,表现为随机线程挂起
  • 若 native DLL 使用了静态全局变量或单例,高并发下调用它极易触发隐式锁,这种锁 WinDbg 和 PerfView 都无法直接标记归属,只能靠排除法 + 源码审查
  • PerfView 导出的 GCStats 中若 Gen 0 PauseMSec 极短但 Gen 2 却很长,且 native 栈频繁出现 HeapAlloc,说明托管内存压力已传导至 native heap 管理层
实际排查时,别指望一次抓 dump 或一次采样就定位根因。线程状态、GC 频率、native 调用耗时这三者往往互相掩盖,得来回切换工具交叉验证。尤其是 async 场景下,Task 状态、SynchronizationContext、线程池队列深度这三者的关联性,必须同时看全才能看清真相。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

403

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

528

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

306

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

74

2025.09.10

全局变量怎么定义
全局变量怎么定义

本专题整合了全局变量相关内容,阅读专题下面的文章了解更多详细内容。

73

2025.09.18

python 全局变量
python 全局变量

本专题整合了python中全局变量定义相关教程,阅读专题下面的文章了解更多详细内容。

96

2025.09.18

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

367

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

561

2023.08.10

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WEB前端教程【HTML5+CSS3+JS】
WEB前端教程【HTML5+CSS3+JS】

共101课时 | 8.1万人学习

JS进阶与BootStrap学习
JS进阶与BootStrap学习

共39课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号