ClrMD 是什么,什么时候该用它
ClrMD 是微软官方提供的 .NET 内存分析 SDK,专用于离线解析
dump文件(如 Windows 的
full dump或
minidump),不是实时调试工具,也不能替代 Visual Studio 或 dotMemory。它适合在没有图形界面、无法装调试器的服务器环境里,用代码批量分析崩溃或内存泄漏问题。
如果你拿到的是
*.dmp文件,且想写 C# 程序自动提取 GC 堆对象统计、查找大对象、定位
String泄漏、枚举线程调用栈——ClrMD 就是目前最直接的选择。
安装与基础初始化要注意什么
必须匹配目标 dump 的 .NET 运行时版本:.NET Framework 4.x dump 要用
Microsoft.Diagnostics.Runtime2.x;.NET Core/.NET 5+ dump 必须用 2.2.0+(推荐 2.2.416702),否则会报
Failed to initialize runtime或直接抛
NullReferenceException。 用 NuGet 安装:
Install-Package Microsoft.Diagnostics.Runtime(注意选对版本) 确保程序运行时架构(x64/x86)和 dump 架构一致;x64 dump 在 x86 进程里打开会失败 初始化前检查符号路径:
DataTarget.LoadCrashDump不自动下载 PDB,需手动设
SymbolLocator或提前把 PDB 放到 dump 同目录
怎么读取托管堆并找出可疑大对象
核心流程是:加载 dump → 获取 CLR 运行时 → 枚举所有对象 → 按类型/大小筛选。别直接调
Heap.EnumerateObjects(),它返回的是地址,得配合
GetObjectSize()和
GetTypeByMethodTable()才能还原类型信息。
常见误操作是遍历全部对象再过滤,实际应先用
Heap.FindObjectByType()或
Heap.EnumerateObjectAddressesByType()缩小范围,否则在 10GB+ dump 里可能卡死或 OOM。 查所有
byte[]实例大小总和:
heap.EnumerateObjectAddressesByType("System.Byte[]").Sum(addr => heap.GetObjectSize(addr))
找单个大于 10MB 的字符串:heap.EnumerateObjectAddressesByType("System.String").FirstOrDefault(addr => heap.GetObjectSize(addr) > 10 * 1024 * 1024)
获取对象内容(如字符串值)需用 ReadObject<string>(addr)</string>,但仅当对象未被压缩/移动且类型可序列化时才安全
线程栈和异常信息怎么提取
ClrMD 不提供“一键导出所有线程完整调用栈”的封装方法,必须手动遍历每个线程的
StackWalk,且 .NET Core dump 中栈帧可能缺少托管方法名(尤其没带 PDB 时)。关键点在于:先用
Runtime.Threads获取线程列表,再对每个线程调
GetStackTrace(),但返回的
ClrStackFrame里
Method字段常为空,得靠
Module+
IP+ 符号定位反推。 只读托管异常信息(如最后未处理异常):
crashInfo.Exception(仅 full dump 有,minidump 通常为空) 查某线程是否在执行
HttpClient.SendAsync:
thread.GetStackTrace().Any(f => f.Method?.Contains("SendAsync") == true)
若 Method为 null,尝试用
f.Module.TryGetSymbolName(f.IP, out string name)补充,但依赖 PDB 可用性
真正难的是跨 native/managed 边界的栈帧识别,ClrMD 默认不解析 native 帧,遇到
clr!JIT_Throw或
ntdll!NtWaitForSingleObject就断了——这时候只能结合 WinDbg 的
!clrstack -a做交叉验证。
