本文介绍了复杂的SOS.dll ObjSize和DumpObject.如何在C#中重新创建SOS.dll?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题主要基于我以前的帖子.

我正在尝试使用反射重新创建SOS.dll的某些功能.特别是ObjSizeDumpObject命令.我使用反射来查找所有字段,然后如果这些字段是基本类型,则将基本类型的大小添加到对象的整体大小中.如果该字段是值类型,则我递归地调用原始方法并遍历引用树,直到找到所有基本类型字段为止.

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

  • 水桶
  • 条目
  • 计数
  • 版本
  • freeList
  • freeCount
  • 比较器
  • _syncRoot
  • m_siInfo

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

  • 版本名称
  • HashSizeName
  • KeyValuePairsName
  • 比较器名称

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

  • 字典-532B

然后我在Dictionary上调用DumpObject以获得其引用类型的内存地址.然后,当我对它的引用类型调用Objsize时,我得到:

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

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

此外,我在上面链接的线程中未曾回答过我的一个问题.我在问评估对象的内存大小时是否应该忽略属性.普遍的共识是不理会他们.但是,我找到了一个很好的示例,说明从Type.GetFields()返回的集合中何时不包含属性的后备字段.在查看字符串的内容时,您将具有以下内容:

对象包含名为FirstChar的属性对象包含名为Chars的属性对象包含名为Length的属性对象包含名为m_stringLength的字段对象包含名为m_firstChar的字段对象包含名为空的字段对象包含名为TrimHead的字段对象包含名为TrimTail的字段对象包含名为TrimBoth的字段对象包含名为charPtrAlignConst的字段对象包含名为alignConst的字段m_firstCharm_stringLength是属性FirstCharLength的后备字段,但是字符串的实际内容保留在Chars属性中.这是一个索引属性,可以对其进行索引以返回String中的所有字符,但是我找不到包含字符串字符的相应字段.

有什么想法吗?还是如何获取索引属性的后备字段?应该在内存大小中包括索引属性吗?

解决方案

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

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

CLR实际上利用了无法发现布局的事实.如果后面的字段适合其他两个字段之间的填充,它将交换字段.如果您知道对齐规则,则生成的对象将小于生成的对象.尝试进行逆向工程是一项艰巨的工作,它还取决于体系结构(x86与x64与Arm).

SOS.dll没问题,它可以直接访问CLR为某个类维护的内部数据.不受限制的托管代码.

This question is largely based on my previous post found here.

I'm attempting to recreate some of the functionality of the SOS.dll using reflection. Specifically the ObjSize and DumpObject commands. I use reflection to find all the fields and then if the fields are primitive types I add the size of the primitive type to the overall size of the object. If the field is a value type, then I recursively call the original method and walk down the reference tree until I've hit all primitive type fields.

I'm consistently getting object sizes larger than SOS.dll ObjSize command by a factor of two or so. One reason I've found is that my reflection code seems to be finding fields that SOS is ignoring. For example in a Dictionary, SOS find's the following fields:

  • buckets
  • entries
  • count
  • version
  • freeList
  • freeCount
  • comparer
  • keys
  • values
  • _syncRoot
  • m_siInfo

However my reflection code finds all of the above and also finds:

  • VersionName
  • HashSizeName
  • KeyValuePairsName
  • ComparerName

Also, I'm getting confused regarding the inconsistencies found in the SOS ObjSize and DumpObject commands. I know DumpObject doesn't look at the size of the referenced types. However when I call Object size on the dictionary mentioned above I get:

  • Dictionary - 532B

Then I call DumpObject on the Dictionary to get the memory address of it's reference types. Then when I call Objsize on it's reference types I get:

  • buckets - 40
  • entries - 364
  • comparer - 12
  • keys - 492
  • (the rest are null or primitive)

Shouldn't the ObjSize on the top level dictionary roughly be the sum of all the ObjSizes on fields within the dictionary? Why is Reflection finding more fields that DumpObject? Any thoughts on why my reflection analysis is returning numbers larger than SOS.dll?

Also, I never got an answer to one of my questions asked in the thread linked above. I was asking whether or not I should ignore properties when evaluating the memory size of an object. The general consensus was ignore them. However, I found a good example of when a property's backing field would not be included in the collection returned from Type.GetFields(). When looking under the hood of a String you have the following:

Object contains Property named FirstCharObject contains Property named CharsObject contains Property named LengthObject contains Field named m_stringLengthObject contains Field named m_firstCharObject contains Field named EmptyObject contains Field named TrimHeadObject contains Field named TrimTailObject contains Field named TrimBothObject contains Field named charPtrAlignConstObject contains Field named alignConstThe m_firstChar and m_stringLength are the backing fields of the Properties FirstChar and Length but the actual contents of the string are held in the Chars property. This is an indexed property that can be indexed to return all the chars in the String but I can't find a corresponding field that holds the characters of a string.

Any thoughts on why that is? Or how to get the backing field of the indexed property? Should indexed properties be included in the memory size?

解决方案

Well, your Reflection code is broken. The 4 members you mention (VersionName etc) are not fields, they are private constants. I'm guessing you are using Type.GetMembers() instead of Type.GetFields() and not checking the returned MemberInfo.MemberType properly. Just use GetFields() instead.

Do note that you can never get the correct size of a managed object. The layout of the object is undiscoverable. The size is not the sum of the fields, fields are aligned. Pretty similar to the StructLayout.Pack property. Alignment can create holes in the layout, so-called "padding bytes". As well as extra padding at the end to get fields aligned when the class object is stored in an array.

The fact that layout is not discoverable is actually taken advantage of by the CLR. It will swap fields if a later field fits in the padding between two other fields. Producing a smaller object than what you'd get if you knew the alignment rules. Trying to reverse-engineer this is a perilous endeavor, it also depends on the architecture (x86 vs x64 vs Arm).

SOS.dll doesn't have that problem, it has direct access to the internal data that the CLR maintains for a class. Off limits to managed code.

这篇关于复杂的SOS.dll ObjSize和DumpObject.如何在C#中重新创建SOS.dll?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-27 13:35
查看更多