如何在C#托管代码中密切重新创建SOS.dll功能? SOS.dll ObjSize和DumpObject如何工作?

时间:2013-01-30 16:37:01

标签: memory reflection clr sos

我对这个话题进行了很多研究,但仍然很难过。我之前已经向stackOverflow提出了这个问题并收到了不太令人满意的回复。这让我相信这是一个相当高级的话题,需要对CLR的强烈理解才能回答。我希望大师可以帮助我。

此问题主要基于我之前发现的帖子herehere

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

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

  • 水桶
  • 条目
  • 计数
  • 版本
  • 空闲列表
  • freeCount
  • 比较器
  • _syncRoot
  • m_siInfo

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

  • 版本名称
  • HashSizeName
  • KeyValuePairsName
  • ComparerName

之前的回答暗示这些是常数而不是字段。常量不在记忆中吗?我应该忽略常数吗?我不确定使用哪些绑定标志来获取常量以外的所有字段...

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

  • 字典 - 532B

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

  • 水桶 - 40
  • 条目 - 364
  • comparer - 12
  • keys - 492
  • (其余为空或原始)

**顶级词典上的ObjSize不应该大致是字典中所有ObjSizes字段的总和吗?为什么Reflection会发现DumpObject的更多字段?有关为什么我的反射分析返回的数字大于SOS.dll的想法? **

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

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

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

对此为何的任何想法?或者如何获取索引属性的支持字段?索引属性是否应包含在内存大小中?

1 个答案:

答案 0 :(得分:2)

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

对于引用类型,CLR本身会为每个实例(MT和syncblk)添加内部字段。这些不是Reflection API的表现。另外,CLR可以根据类型的定义在字段的存储上使用任何种类的填充/压缩。这意味着原始类型的大小在不同的引用类型中可能不一致。反射也不允许你发现它。

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