c# - SOS.dll ObjSize 和 DumpObject 背后的复杂之处。如何在 C# 中重新创建 SOS.dll?

标签 c# memory reflection sos

这个问题很大程度上是基于我之前找到的帖子 here .

我正在尝试使用反射重新创建 SOS.dll 的一些功能。特别是 ObjSizeDumpObject 命令。我使用反射来查找所有字段,然后如果字段是原始类型,我将原始类型的大小添加到对象的整体大小。如果该字段是值类型,那么我将递归调用原始方法并沿着引用树向下走,直到找到所有原始类型字段。

我一直得到比 SOS.dll ObjSize 命令大两倍左右的对象大小。我发现的原因之一是我的反射代码似乎在寻找 SOS 忽略的字段。例如在字典中,SOS 查找以下字段:

  • 条目
  • 计数
  • 版本
  • 自由列表
  • 免费计数
  • 比较者
  • key
  • 值(value)观
  • _syncRoot
  • m_siInfo

但是我的反射代码找到了以上所有内容并且还找到了:

  • 版本名称
  • 哈希大小名
  • 键值对名称
  • 比较器名称

此外,我对在 SOS ObjSize 和 DumpObject 命令中发现的不一致感到困惑。我知道 DumpObject 不会查看引用类型的大小。但是,当我在上面提到的字典上调用对象大小时,我得到:

  • 字典 - 532B

然后我在 Dictionary 上调用 DumpObject 来获取它的引用类型的内存地址。然后当我在它的引用类型上调用 Objsize 时,我得到:

  • 桶 - 40
  • 条目 - 364
  • 比较器 - 12
  • 键 - 492
  • (其余为空或原始)

顶级字典上的 ObjSize 不应该大致是字典内字段上所有 ObjSize 的总和吗?为什么 Reflection 找到的字段比 DumpObject 多?关于为什么我的反射分析返回的数字大于 SOS.dll 有什么想法吗?

另外,我在上面链接的线程中提出的问题之一从未得到答案。我问的是在评估对象的内存大小时是否应该忽略属性。普遍的共识是忽略它们。但是,我找到了一个很好的示例,说明属性的支持字段何时不包含在从 Type.GetFields() 返回的集合中。在 String 的内部进行查看时,您会发现以下内容:

对象包含名为 FirstChar 的属性 对象包含名为 Chars 的属性 对象包含名为 Length 的属性 对象包含名为 m_stringLength 的字段 对象包含名为 m_firstChar 的字段 对象包含名为 Empty 的字段 对象包含名为 TrimHead 的字段 对象包含名为 TrimTail 的字段 对象包含名为 TrimBoth 的字段 对象包含名为 charPtrAlignConst 的字段 对象包含名为 alignConst 的字段 m_firstCharm_stringLength 是属性 FirstCharLength 的支持字段,但字符串的实际内容是在 Chars 属性(property)中举行。这是一个索引属性,可以对其进行索引以返回字符串中的所有字符,但我找不到包含字符串字符的相应字段。

有什么想法吗?或者如何获取索引属性的支持字段?索引属性是否应包含在内存大小中?

最佳答案

好吧,你的反射代码坏了。您提到的 4 个成员(VersionName 等)不是字段,它们是私有(private)常量。我猜您正在使用 Type.GetMembers() 而不是 Type.GetFields() 并且没有正确检查返回的 MemberInfo.MemberType 。只需使用 GetFields() 即可。

请注意,您永远无法获得托管对象的正确大小。对象的布局是不可发现的。大小不是字段的总和,字段对齐。与 StructLayout.Pack 属性非常相似。对齐会在布局中产生空洞,即所谓的“填充字节”。以及最后的额外填充,以便在类对象存储在数组中时对齐字段。

CLR 实际上利用了布局不可发现的事实。如果后面的字段适合其他两个字段之间的填充,它将交换字段。如果您知道对齐规则,则生成比您得到的对象更小的对象。尝试对此进行逆向工程是一项危险的工作,它还取决于架构(x86 vs x64 vs Arm)。

SOS.dll 没有这个问题,它可以直接访问 CLR 为类维护的内部数据。禁止使用托管代码。

关于c# - SOS.dll ObjSize 和 DumpObject 背后的复杂之处。如何在 C# 中重新创建 SOS.dll?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14483432/

相关文章:

c# - 加速此代码 — 192 万亿次 for() 循环

c# - 从 WPF 中的任何/未指定的父容器断开元素

memory - 如何在 OpenCL 中使用固定内存/映射内存

typescript 循环遍历类类型属性

Java : Is it possible to deserialize objects without reflection

java - 获取泛型参数的泛型父类(super class)

c# - 应用(服务)自升级?

c# - 如何使用 oledb 将记录插入 Access 表?

android - Galaxy S 3 内存问题

C++ 继承 : does lack of virtual destructor lead to memory leak?