【发布时间】:2026-01-31 11:15:02
【问题描述】:
我有一些非常大的托管进程的内存转储,我试图从中获取大量统计信息,并且能够呈现堆上相当深的对象图的交互式视图.想想类似于!do <address> 和prefer_dml 1 在WinDbg 中设置的SOS,您可以在其中不断单击属性并查看它们的值,只有在比较许多对象的更友好的UI 中。
我发现 Microsoft.Diagnostics.Runtime (ClrMD) 特别适合这项任务,但我很难处理数组字段,而且我有点困惑关于对象字段,我做得更好一点。
数组:
如果我将一个地址直接从堆中取出的数组作为目标并使用ClrType.GetArrayLength 和ClrType.GetArrayElementValue 一切正常,但是一旦我挖掘另一个对象上的字段,我不确定我从中得到什么价值ClrInstanceField.GetValue 当ClrInstanceField.ElementType 是ClrElementType.SZArray 时(我还没有遇到Array 在我的对象图中挖掘,但我也想处理它)。
编辑: 我刚刚决定使用ClrType for System.UInt64 来取消引用数组字段(使用parent address + offset of the array field 来计算数组指针所在的地址已存储),然后我可以像从 EnumerateObjects 获得它一样使用它。我现在在一些不支持 ArrayComponentType 属性的数组上遇到了一些困难。我还没有使用结构数组进行测试,所以我也想知道这是否会像int[] 那样是 C 风格的内联结构分配,或者它是否是指向堆上结构的指针数组。 Guid[] 是我在获取 ArrayComponentType 时遇到问题的类型之一。
对象:已修复(逻辑错误)
有了Type 和ClrElementType.Object 的ClrInstanceField,我得到了更好的结果,但还需要更多。首先,在调用GetFieldValue 之后,我得到了一个ulong 地址(?),我可以使用ClrInstanceField.Type.Fields 来处理它,所以我可以看到嵌套对象的字段名称和值。也就是说,我必须考虑多态性,所以我尝试在同一个地址上使用ClrHeap.GetObjectType,它要么返回NULL,要么完全不正确。该地址在我的第一个用例中有效,但在第二个用例中无效,这似乎很奇怪。
字符串:已修复(找到解决方法)
因为我的真实项目已经使用了带 SOS 的 DbgEng,所以我有一种不同的方法可以通过地址轻松获取字符串的值,但是尝试使用 ClrInstanceField.GetFieldValue 成功返回一个字符串似乎很奇怪,但完全结果不准确(一堆奇怪的字符)。也许我做错了?
编辑:我已经从我的原始代码中提取了一个现在在 LINQPad 中运行的抽象。在这里发帖有点长,但都是here in a gist。所有的复制/粘贴/重构仍然有点混乱,我会进一步清理它,在解决这些问题后,可能会在 CodePlex 或 GitHub 上发布最终源代码。
代码库相当大,并且是针对某个项目的,但如果绝对有必要,我可以提取一个示例集。也就是说,对 ClrMD 对象的所有访问都相当简单。我从诸如!dumpheap -stat 之类的SOS 命令中获取初始地址(这对根对象工作正常),然后我使用ClrHeap.GetTypeByName 或ClrHeap.GetObjectType。之后,它完全依赖于ClrType.Fields 和ClrInstanceField 成员Type、ElementType 和GetFieldValue
作为额外的奖励,我确实找到了随 NuGet 包提供的 XML 文档的 browser friendly 版本,尽管它与 IntelliSense 提供的文档相同。
【问题讨论】:
-
我在 ClrMD 之上创建了一个扩展库,可以很好地与 LINQPad 集成,如果您有兴趣,请查看:github.com/JeffCyr/ClrMD.Extensions