Get 'optimistic' size of managed object in memory

匿名 (未验证) 提交于 2019-12-03 02:31:01

问题:

First, I am aware of many posted questions covering the topic: 1 2 3 4 5. The proposed approaches & why not:

  • Marshal.SizeOf() 1 - does not work for managed types.
  • GC.GetTotalMemory 1 2 - race condition prone.
  • Serialization 1 2 3 4 - it's quite close, but automatic-fields can be problematic, as well as properties without public setter. Also, it's not optimal performance-wise.
  • Code profiling using SOS 1 2 and other tools - great, but not for runtime.

Due to padding and issues posted 1 2 3, it appears, there is no optimal solution, rather trade-off between precision, performance and code-bloat.

However, I needed simple method to count optimistic (minimum) memory usage, i.e. I want to know that the object occupies at least that much. The rationale is that I have the environment of types owning many collections, sometimes nested, and I want to approximate quickly, that an object is coming close to i.e. .5 GB in memory etc..

There is what I came up with & my questions:

  1. I am looking forward for Your thoughts and suggestions on what can be done better.
  2. Especially, I'm looking for memory that is not accounted for in this code, and could be (without writing 200+ lines of code for this).
  3. I can't get auto generated fields (property __BackingField) for inherited type, whilst it works fine for not inherited backing fields. I searched for proper BindingFlag, but could not find one.

    public static long SizeInBytes<T>(this T someObject) {     var temp = new Size<T>(someObject);     var tempSize = temp.GetSizeInBytes();     return tempSize; }  /// <summary> ///     A way to estimate the in-memory size af any menaged object /// </summary> /// <typeparam name="TT"></typeparam> private sealed class Size<TT> {     private static readonly int PointerSize = Environment.Is64BitOperatingSystem         ? sizeof(long)         : sizeof(int);      private readonly TT _obj;     private readonly HashSet<object> _references;      public Size(TT obj)     {         _obj = obj;         _references = new HashSet<object> { _obj };     }      public long GetSizeInBytes()     {         return GetSizeInBytes(_obj);     }      private long GetSizeInBytes<T>(T obj)     {         if (obj == null) return sizeof(int);         var type = obj.GetType();          if (type.IsPrimitive)         {             switch (Type.GetTypeCode(type))             {                 case TypeCode.Boolean:                 case TypeCode.Byte:                 case TypeCode.SByte:                     return sizeof(byte);                 case TypeCode.Char:                     return sizeof(char);                 case TypeCode.Single:                     return sizeof(float);                 case TypeCode.Double:                     return sizeof(double);                 case TypeCode.Int16:                 case TypeCode.UInt16:                     return sizeof(short);                 case TypeCode.Int32:                 case TypeCode.UInt32:                     return sizeof(int);                 case TypeCode.Int64:                 case TypeCode.UInt64:                 default:                     return sizeof(long);             }         }         if (obj is decimal)         {             return sizeof(decimal);         }         if (obj is string)         {             return sizeof(char) * obj.ToString().Length;         }         if (type.IsEnum)         {             return sizeof(int);         }         if (type.IsArray)         {             long sizeTemp = PointerSize;             var casted = (IEnumerable)obj;             foreach (var item in casted)             {                 sizeTemp += GetSizeInBytes(item);             }             return sizeTemp;         }         if (obj is Pointer)         {             return PointerSize;         }         long size = 0;         var t = type;         while (t != null)         {             size += PointerSize;             var fields =                 t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic |                             BindingFlags.DeclaredOnly);             foreach (var field in fields)             {                 var tempVal = field.GetValue(obj);                 if (!_references.Contains(tempVal))                 {                     _references.Add(tempVal);                     size += GetSizeInBytes(tempVal);                 }             }             t = t.BaseType;         }         return size;     } } 

[EDIT]

This question resulted in Nuget and cp article

回答1:

To answer your third question about getting fields, you can reliably get all fields in a type like this:

    public static IEnumerable<FieldInfo> GetAllFields(Type t)     {         while (t != null)         {             foreach (FieldInfo field in t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly))             {                 yield return field;             }             t = t.BaseType;         }     } 

This works because GetFields can return the private fields of the current Type, but not any inherited private fields; so you need to walk up the inheritance chain calling GetFields on each Type.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!