获取';乐观的';内存中托管对象的大小

本文关键字:对象 内存 乐观 获取 | 更新日期: 2023-09-27 18:29:59

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

  • Marshal.SizeOf()1-不适用于托管类型
  • GC.GetTotalMemory 1 2-容易出现比赛情况
  • 序列化1 2 3 4-非常接近,但是自动字段和没有公共设置者。此外,它并不是最佳性能
  • 使用SOS 1 2和其他工具进行代码分析-很棒,但不适用于运行时

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

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

这是我想出的&我的问题:

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

[EDIT]

这个问题导致了Nuget和cp文章

获取';乐观的';内存中托管对象的大小

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

    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的私有字段,但不能返回任何继承的私有字段;因此,您需要在每个Type上调用GetFields的继承链上遍历。