10

Is there a way to programmatically and accurately determine the amount of memory used by an object in c#? I am not concerned with how slow the process is, so running GCs left and right is acceptable (of course I'd prefer something more efficient).

  • Serializing objects and looking at the resulting length doesn't seem very accurate (in a simple test of this method, I saw that an integer returned a value of 54).

  • Using GC.GetTotalMemory(true) seems to produce inconsistent values, not to mention they appear too large.

  • Using Marshal.SizeOf(object) produces accurate results, but only appears to work with primitives.

If nothing along those lines is available, an alternative would be to calculate sizes based on the structures used and the primitives involved. This would also be acceptable (though upsetting), but I'd need to know the correct method of calculating object overheads, etc. Any literature that would show me a way to do this would be awesome.

Similar SO questions (none of which seemed to have concrete methods for accurate calculation of object size):

How much memory does a C#/.NET object use?

How to get memory available or used in C#

How to get object size in memory?

sizeof() equivalent for reference types?

Tools to profile memory (non-programmatic approach):

http://www.microsoft.com/en-us/download/details.aspx?id=16273

Find out how much memory is being used by an object in C#?

Community
  • 1
  • 1
JesseBuesking
  • 6,496
  • 4
  • 44
  • 89

2 Answers2

3

another idea is to have a helper class to do this by reflecting the object and extract its all data members and gather all fields size by sizeof() , it will be a little bit complicated but it is implementable

this class will calculate the actual size of an object , but i have tested it just a few times and a few objects were tested but i think i will be working.

public class SizeHelper
{
    private static int GetTypeSizeArray(string typeName, object objValue)
    {
        switch (typeName)
        {
            case "System.Double[]":
                return sizeof(System.Double) * ((System.Double[]) objValue).Length ;
            case "System.Single[]":
                return sizeof(System.Single) * ((System.Single[])objValue).Length;
            case "System.Char[]":
                return sizeof(System.Char) * ((System.Char[])objValue).Length;
            case "System.Int16[]":
                return sizeof(System.Int16) * ((System.Int16[])objValue).Length;
            case "System.Int32[]":
                return sizeof(System.Int32) * ((System.Int32[])objValue).Length;
            case "System.Int64[]":
                return sizeof(System.Int64) * ((System.Int64[])objValue).Length;
            case "System.UInt16[]":
                return sizeof(System.UInt16) * ((System.UInt16[])objValue).Length;
            case "System.UInt32[]":
                return sizeof(System.UInt32) * ((System.UInt32[])objValue).Length;
            case "System.UInt64[]":
                return sizeof(System.UInt64) * ((System.UInt64[])objValue).Length;
            case "System.Decimal[]":
                return sizeof(System.Decimal) * ((System.Decimal[])objValue).Length;
            case "System.Byte[]":
                return sizeof(System.Byte) * ((System.Byte[])objValue).Length;
            case "System.SByte[]":
                return sizeof(System.SByte) * ((System.SByte[])objValue).Length;
            case "System.Boolean":
                return sizeof (System.Boolean)*((System.Boolean[]) objValue).Length;
            default:
                return 0;
        }
    }

    public static int GetSize(object obj)
    {
        Type t = obj.GetType();

        FieldInfo[] fields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
        int size = 0;
        foreach (FieldInfo fieldInfo in fields)
        {
            if (fieldInfo.FieldType.BaseType.FullName.Equals("System.ValueType"))
            {
                size += GetTypeSize(fieldInfo.FieldType.FullName);
            }
            else if (fieldInfo.FieldType.BaseType.FullName.Equals("System.Array"))
            {
                var subObj = fieldInfo.GetValue(obj);
                if (subObj != null)
                    size += GetTypeSizeArray(fieldInfo.FieldType.FullName, subObj);
            }
            else if(fieldInfo.FieldType.FullName.Equals("System.String"))
            {
                var subObj = fieldInfo.GetValue(obj);
                if (subObj != null)
                {
                    size += subObj.ToString().Length*sizeof (System.Char);
                }
            }
            else
            {
                var subObj = fieldInfo.GetValue(obj);
                if (subObj != null)
                    size += GetSize(subObj);
            }
        }
        return size;
    }

    private static int GetTypeSize(string typeName)
    {
        switch (typeName)
        {
            case "System.Double":
                return sizeof(System.Double);
            case "System.Single":
                return sizeof(System.Single);
            case "System.Char":
                return sizeof(System.Char);
            case "System.Int16":
                return sizeof(System.Int16);
            case "System.Int32":
                return sizeof(System.Int32);
            case "System.Int64":
                return sizeof(System.Int64);
            case "System.UInt16":
                return sizeof(System.UInt16);
            case "System.UInt32":
                return sizeof(System.UInt32);
            case "System.UInt64":
                return sizeof(System.UInt64);
            case "System.Decimal":
                return sizeof(System.Decimal);
            case "System.Byte":
                return sizeof(System.Byte);
            case "System.SByte":
                return sizeof(System.SByte);
             case "System.Boolean":
                return sizeof (System.Boolean);
            default:
                return 0;
        }
    }
}
nawfal
  • 70,104
  • 56
  • 326
  • 368
Bahram
  • 1,464
  • 2
  • 22
  • 38
  • And then add a fixed size for the header block. But how do you treat strings and other outgoing references? – H H Sep 24 '12 at 06:32
  • 1
    actually strings are char arrays so we can count its length and multiply to sizeof(char) , and for other objects in a given object we can use recursive semantic to get their size. – Bahram Sep 24 '12 at 07:45
  • 1
    This is grand, except I had to add `else if ( fieldInfo.FieldType.IsEnum) { size += GetTypeSize("System.Int32");}` to `GetSize` otherwise it would stackoverflow if it tried to calculate the size of an Enum. – gingerbreadboy May 06 '14 at 09:01
  • 2
    @gingerbreadboy You hopefully check which kind of enum it is. Enum can derive from byte, short or long as well, so it can provide wrong values or cause exceptions to appear once an enum does not derive from int. Use `e2.GetType().GetEnumUnderlyingType();` and you'll get the true length. – SharpShade Nov 16 '14 at 16:10
  • Pretty good, but fieldInfo.FieldType.BaseType.FullName.Equals will throw an error if BaseType is null. Need to handle that. – LarryBud Apr 23 '21 at 02:25
0
object obj = new List<int>(); // whatever you want to get the size of
RuntimeTypeHandle th = obj.GetType().TypeHandle;
int size = *(*(int**)&th + 1);
Console.WriteLine(size);

I dont know whether it is useful to you or not...but try to refer this link...espacialy fig.4

http://msdn.microsoft.com/en-us/magazine/cc163791.aspx#S9

Freelancer
  • 9,008
  • 7
  • 42
  • 81
  • i have add this code , but it is not compiling and this compile error is occurred "*Cannot take the address of, get the size of, or declare a pointer to a managed type*" [at int size = ...] – Bahram Sep 24 '12 at 05:07
  • 1
    @H-Bahrami: you need to compile your code with the `/unsafe` option (in Visual Studio project properties check `Build -> Allow unsafe code`), as well as encapsulate it in an `unsafe { ... }` block. More info: http://msdn.microsoft.com/en-us/library/vstudio/chfa2zb8.aspx – Ian Kemp Jun 16 '13 at 14:36