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

C# 获取';乐观的';内存中托管对象的大小,c#,.net,memory,memory-management,C#,.net,Memory,Memory Management,首先,我知道有很多问题都涉及到这个主题:。提议的方法&为什么不: Marshal.SizeOf()-不适用于托管类型 GC.GetTotalMemory-易受竞争条件影响 序列化-非常接近,但是 自动字段可能会有问题,没有自动字段的属性也可能会有问题 公众设定者。此外,它在性能方面也不是最优的 使用SOS进行代码分析- 很好,但不适用于运行时 由于填充和发布的问题,似乎没有最佳解决方案,而是在精度、性能和代码膨胀之间进行权衡 然而,我需要一种简单的方法来计算乐观(最小)内存使用量,即我想知道

首先,我知道有很多问题都涉及到这个主题:。提议的方法&为什么不:

  • Marshal.SizeOf()-不适用于托管类型
  • GC.GetTotalMemory-易受竞争条件影响
  • 序列化-非常接近,但是 自动字段可能会有问题,没有自动字段的属性也可能会有问题 公众设定者。此外,它在性能方面也不是最优的
  • 使用SOS进行代码分析- 很好,但不适用于运行时
由于填充和发布的问题,似乎没有最佳解决方案,而是在精度、性能和代码膨胀之间进行权衡

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

下面是我的想法和我的问题:

  • 我期待着您的想法和建议 做得更好
  • 特别是,我要寻找的记忆不是 在这段代码中说明,并且可以是(不写200多行 这方面的代码)
  • 我无法获取自动生成的字段(属性
    \uuuu 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;
        }
    }
    
    publicstaticlong-SizeInBytes(这个T-someObject)
    {
    var temp=新大小(someObject);
    var tempSize=temp.GetSizeInBytes();
    返回时间长度;
    }
    /// 
    ///一种估计任何菜单对象的内存大小的方法
    /// 
    /// 
    私人密封班级人数
    {
    私有静态只读int PointerSize=Environment.Is64BitOperatingSystem
    ?尺寸(长)
    :sizeof(int);
    私有只读TT_obj;
    私有只读哈希集\u引用;
    公共规模(TT obj)
    {
    _obj=obj;
    _references=newhashset{u obj};
    }
    公共长GetSizeInBytes()
    {
    返回GetSizeInBytes(_obj);
    }
    专用长GetSizeInBytes(T obj)
    {
    if(obj==null)返回sizeof(int);
    var type=obj.GetType();
    if(类型为IsPrimitive)
    {
    开关(类型.GetTypeCode(类型))
    {
    大小写类型代码。布尔值:
    大小写类型代码。字节:
    案例类型代码.SByte:
    返回sizeof(字节);
    case TypeCode.Char:
    返回sizeof(char);
    案例类型代码。单个:
    返回sizeof(浮动);
    案例类型代码。双:
    返回sizeof(双倍);
    case TypeCode.Int16:
    案例类型代码.UInt16:
    返回sizeof(短);
    case TypeCode.Int32:
    案例类型代码.UInt32:
    返回sizeof(int);
    case TypeCode.Int64:
    案例类型代码.UInt64:
    违约:
    返回sizeof(长);
    }
    }
    if(对象为十进制)
    {
    返回sizeof(十进制);
    }
    if(obj是字符串)
    {
    返回sizeof(char)*obj.ToString().Length;
    }
    if(type.IsEnum)
    {
    返回sizeof(int);
    }
    如果(键入IsArray)
    {
    long-sizeTemp=PointerSize;
    var casted=(IEnumerable)obj;
    foreach(铸造中的var项目)
    {
    sizeTemp+=GetSizeInBytes(项目);
    }
    返回sizeTemp;
    }
    if(obj是指针)
    {
    返回点个性化;
    }
    长尺寸=0;
    var t=类型;
    while(t!=null)
    {
    大小+=指针大小;
    变量字段=
    t、 GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic|
    绑定标志(仅限声明);
    foreach(字段中的变量字段)
    {
    var tempVal=field.GetValue(obj);
    if(!\u references.Contains(tempVal))
    {
    _参考文献。添加(tempVal);
    size+=GetSizeInBytes(tempVal);
    }
    }
    t=t.BaseType;
    }
    返回大小;
    }
    }
    
  • [编辑]


    此问题导致和

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

        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;
            }
        }
    
    公共静态IEnumerable GetAllFields(类型t)
    {
    while(t!=null)
    {
    foreach(t.GetFields中的FieldInfo字段(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly))
    {
    产量回报场;
    }
    t=t.BaseType;
    }
    }
    

    这是因为
    GetFields
    可以返回当前
    类型的私有字段,但不能返回任何继承的私有字段;因此,您需要沿着继承链向上走,在每个
    类型上调用
    GetFields

    可能太宽,因为“可能的答案太多,或者好的答案对于这种格式来说太长。请添加详细信息以缩小答案集,或者隔离一个可以在几段中回答的问题”与所有条件语句相比,使用重载来处理基元类型可能更简单。您应该将
    GetTypeCode
    与switch/case一起使用,以避免出现瀑布式的情况。尝试使用循环引用计算对象的大小总是很有趣的…:)在计算字符串的大小时,还必须考虑长度成员的大小。因此,它应该是sizeof(char)*string.Length+sizeof(int)。