在内存中获取托管对象的“乐观”大小

时间:2014-11-10 02:02:06

标签: c# .net memory memory-management

首先,我知道许多已发布的问题涉及该主题:1 2 3 4 5。提出的方法&为什么不:

  • Marshal.SizeOf()1 - 不适用于托管类型。
  • GC.GetTotalMemory 1 2 - 容易出现种族情况。
  • 序列化1 2 3 4 - 它非常接近,但是 自动字段可能有问题,以及没有的属性 公共二传手。而且,它在性能方面并不是最佳的。
  • 使用SOS进行代码分析1 2 and other tools - 很棒,但不适合运行时。

由于填充和问题已发布1 2 3,似乎没有最佳解决方案,而是在精度,性能和代码膨胀之间进行权衡。

但是,我需要简单的方法来计算乐观(最小)内存使用量,即我想知道该对象至少占用那么多。理由是我有类型的环境拥有许多集合,有时嵌套,我想快速近似,一个对象接近,即内存中的.5 GB等。

我想出的是&我的问题:

  1. 我期待着你的想法和建议 做得更好。
  2. 特别是,我正在寻找不是的记忆 在这段代码中占了一席之地,并且可以(没有写200多行 这个代码)。
  3. 我无法获得自动生成的字段(属性 __BackingField)对于继承类型,虽然它适用于未继承的支持字段。我搜索了正确的BindingFlag,但找不到一个。

    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;
        }
    }
    
  4. [编辑]

    此问题产生了Nugetcp article

1 个答案:

答案 0 :(得分:3)

要回答关于获取字段的第三个问题,您可以可靠地获取如下所示的所有字段:

    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;
        }
    }

这是有效的,因为GetFields可以返回当前Type的私有字段,但不能返回任何继承的私有字段;所以你需要走在每个GetFields上调用Type的继承链上。