1

我已经对这个主题进行了很多研究,但仍然感到困惑。我之前曾向 stackOverflow 提出过这个问题,但得到的答复并不令人满意。这让我相信这是一个相当高级的话题,需要对 CLR 有深刻的理解才能回答。希望高手能帮帮我。

这个问题主要基于我以前在此处此处找到的帖子。

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

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

  • 水桶
  • 条目
  • 数数
  • 版本
  • 自由清单
  • 自由计数
  • 比较器
  • 钥匙
  • 价值观
  • _syncRoot
  • m_siInfo

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

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

先前的答案暗示这些是常量而不是字段。常量不是保存在内存中吗?我应该忽略常量吗?我不确定使用哪些绑定标志来获取除常量之外的所有字段......

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

  • 字典 - 532B

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

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

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

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

  • 对象包含名为 FirstChar 的属性
  • 对象包含名为 Chars 的属性
  • 对象包含名为 Length 的属性
  • 对象包含名为 m_stringLength 的字段
  • 对象包含名为 m_firstChar 的字段
  • 对象包含名为 Empty 的字段
  • 对象包含名为 TrimHead 的字段
  • 对象包含名为 TrimTail 的字段
  • 对象包含名为 TrimBoth 的字段
  • 对象包含名为 charPtrAlignConst 的字段
  • 对象包含名为 alignConst 的字段

m_firstChar 和 m_stringLength 是属性 FirstChar 和 Length 的支持字段,但字符串的实际内容保存在 Chars 属性中。这是一个索引属性,可以对其进行索引以返回字符串中的所有字符,但我找不到保存字符串字符的相应字段。

关于为什么会这样的任何想法?或者如何获取索引属性的支持字段?索引属性是否应该包含在内存大小中?

4

1 回答 1

2

虽然这个想法很有趣,但我相信它最终是徒劳的,因为反射不提供对对象实际存储的访问。反射允许您查询类型,但不能查询它们的实际内存表示(这是 CLR 的实现细节)。

对于引用类型,CLR 本身向每个实例(MT 和 syncblk)添加内部字段。这些不是由反射 API 显示的。此外,CLR 可以根据类型的定义对字段的存储使用任何类型的填充/压缩。这意味着原始类型的大小在不同的引用类型之间可能不一致。反射也不允许您发现这一点。

简而言之,Reflection 无法发现产生正确结果所需的大量细节。

于 2013-01-30T16:51:03.430 回答