1

I have done a lot of research on this topic and am still stumped. I've previously asked this question to stackOverflow and recieved a less than satisfactory response. This leads me to believe that this is a rather advanced topic needing a strong understanding of the CLR to answer. I'm hoping a guru can help me out.

This question is largely based on my previous posts found here and 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

A previous answer implied that these were constants and not fields. Are constants not held in memory? Should I be ignoring constants? I'm not sure which binding flags to use to get all fields other than constants also...

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 FirstChar
  • Object contains Property named Chars
  • Object contains Property named Length
  • Object contains Field named m_stringLength
  • Object contains Field named m_firstChar
  • Object contains Field named Empty
  • Object contains Field named TrimHead
  • Object contains Field named TrimTail
  • Object contains Field named TrimBoth
  • Object contains Field named charPtrAlignConst
  • Object contains Field named alignConst

The 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?

Community
  • 1
  • 1
nbelisle11
  • 172
  • 2
  • 10
  • possible duplicate of [SOS.dll ObjSize and DumpObject under the hood intricacies. How to recreate SOS.dll in C#?](http://stackoverflow.com/questions/14483432/sos-dll-objsize-and-dumpobject-under-the-hood-intricacies-how-to-recreate-sos-d) – Hans Passant Jan 30 '13 at 22:35
  • You asked the **exact** same question. Why on Earth do you think you'll get a different answer? – Hans Passant Jan 30 '13 at 22:37

1 Answers1

2

While this idea is interesting I believe it is ultimately futile as Reflection doesn't provide access to the actual storage of objects. Reflection lets you query types, but not their actual memory representation (which is an implementation detail of the CLR).

For reference types the CLR itself adds internal fields to each instance (MT and syncblk). These are not surfaced by the Reflection APIs. Additionally the CLR may use any kind of padding/compacting on the storage of the fields depending on the definition of the type. What this means is that size of primitive types may not be consistent across different reference types. Reflection doesn't allow you to discover this either.

In short Reflection is unable to discover a lot of details needed to produce correct results.

Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317