Because of .NET's garbage-collected nature, it's somewhat difficult to measure how much memory is really being used. If you want to measure the size of a class instance, for example, does it include the memory used by instances that your instance points to?
If the answer is no, add up the size of all of the fields:
Using reflection, iterate through all members of the class; use Marshal.Sizeof(member.Type) for anything that typeof(ValueType).IsAssignableFrom(member.Type) - this measures primitive types and structs, all of which reside in the class's instance allocation. Every reference type (anything that isn't assignable to a valuetype) will take IntPtr.Size. There are a disgusting number of exceptions to this, but it might work for you.
If the answer is yes, you have a serious problem. Multiple things can reference a single instance, so if 'a' and 'b' both point to 'c', then RecursiveSizeOf(a) + RecursiveSizeOf(b)
would be larger than SimpleSizeOf(a) + SimpleSizeOf(b) + SimpleSizeOf(c)
.
Even worse, measuring recursively can lead you down circular references, or lead you to objects you don't intend to measure - if a class is referencing a mutex, for example, that mutex may point to the thread that owns it. That thread may point to all of its local variables, which point to some C# framework structures... you may end up measuring your entire program.
It might help to understand that a garbage-collected language like C# is somewhat "fuzzy" (from a completely non-technical sense) in the way it draws distinctions between objects and units of memory. This is a lot of what Marshal
mitigates - marshaling rules ensure that the struct you're marshaling (or measuring) has a well-defined memory layout, and therefore a well-defined size. In which case, you should be using well-defined, marshalable structs if you intend on using Marhsal.SizeOf().
Another option is serialization. This won't tell you specifically how much memory is in use, but it will give you a relative idea of the size of the object. But again, in order to serialize things, they have to have a well-defined layout (and therefore a well-defined size) - you accomplish by making the class appropriately serializable.
I can post implementation examples if any of the above options appeal to you.