• 如何计算托管对象的大小


    想知道这个,原因就一个:85000bytes以上的对象对于GC性能影响较大。我用sqlprofier监视过一些我们自己的biz entity,不过,runtime监视,不爽。。。

    查了不少资料,李建忠老师的blog看了(问题出来了,还没有答案,期待ing...),google了一下:how to get a managed object size?找到了一个blog,在这里:http://blogs.msdn.com/cbrumme/archive/2003/04/15/51326.aspx

    .net不提供SizeOf(manged obj)接口的原因我认为很简单,主要考虑alignment。
    在看这个blog之前,我自己写了一段代码,也能大致的计算出来,不过我考虑得比较简单。
    // 销售订单,内嵌一个客户类
        public class BillOrder
        
    {
            
    private byte[] bindata;
            
    private int num;
            
    private Customer customer;
            
            
    public int id;

            
    public BillOrder(int size)
            
    {
                id 
    = 200;
                bindata 
    = new byte[size];
                
    for(int i=0;i<size;i++)bindata[i] = (byte)'A';
                num 
    = 20;
                customer 
    = new Customer("juqiang",123.4567f);
                
                
                Console.WriteLine(
    "Create a "+size.ToString()+" bytes billorder ");
            }

        }

    // 客户,内嵌一个地址结构体
        public class Customer
        
    {
            
    private float limit;
            
    private string name;
            
    private Address address;

            
    public Customer(string name, float limit)
            
    {
                
    this.name = name;
                
    this.limit = limit;
                address 
    = new Address();
            }

        }

    // 地址结构体
        public struct Address
        
    {
            
    string addr;
            
    string postal;
            
    string contacter;
        }

    生成一个销售订单对象
    BillOrder bo1 = new BillOrder(1000000);

    好了,到此为止,我们要计算bo1的占用内存的大小。看我的第一个原始版本代码:
    // 对于nested对象,递归调用。
    // 这里为了测试,只是简单的用了几个类型
            private static int Calc(object o)
            
    {
                FieldInfo[] filist 
    = o.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
                
    if(filist.Length<1)return -1;
                
                
    int ret = 0;

                
    foreach(FieldInfo fi in filist)
                
    {
                    
    int len = 0;
                    
    int len2 = -1;
                    
    try
                    
    {
                        
    //object o2 = Activator.CreateInstance(fi.GetType());
                        object o2 = fi.GetValue(o);
                        Type t2 
    = o2.GetType();
                        
    if(o2 is System.Int32){len=4;goto end;}
                        
    else if(o2 is System.Byte){len = 1;goto end;}
                        
    else if(o2 is System.Int32[]){len = ((byte[])o2).Length*4;goto end;}
                        
    else if(o2 is System.Byte[]){len= ((byte[])o2).Length;goto end;}
                        
    else if(o2 is System.String){len= Convert.ToString(o2).Length;goto end;}
                        
    else if(o2 is System.Single){len = 4;goto end;}
                        
    else 
                        
    {
                            Console.WriteLine(
    "Below is "+o2.GetType().Name);
                            len2 
    = Calc(o2);
                            Console.WriteLine(
    "end of "+o2.GetType().Name+"(Total) --- "+len2.ToString());
                        }

                    end:
                        ret 
    += len;
                        
    if(len2>-1)ret += len2;
                        Console.WriteLine(fi.Name
    +" --- "+len.ToString());
                    }

                    
    catch
                    
    {

                    }

                }

                
                
                
    return ret;
            }

    利用此方法,这么调用:
    int size = Calc(bo1);
    得到的结果是:
    Create a 1000000 bytes billorder
    Create a 1000000 bytes billorder
    bindata --- 1000000
    num --- 4
    Below is Customer
    limit --- 4
    name --- 7
    Below is Address
    end of Address(Total) --- 0
    address --- 0
    end of Customer(Total) --- 11
    customer --- 0
    id --- 4
    1000019
    就是说,这个对象大小,我这么粗略的计算,是1000019个字节。(上面没有考虑到字节对齐)

    ok,其实上面那篇老外的blog,提到了一嘴,可以clone一个同样的object,然后计算两者指针的偏移。
    然后看这段代码,我废了不少劲才写出来,
    // 首先再new一个同样的销售订单出来
    BillOrder bo1 = new BillOrder(1000000);
    BillOrder bo2 
    = new BillOrder(1000000);
    // 然后。。。
    GCHandle h1  = GCHandle.Alloc(bo1);
    GCHandle h2 
    = GCHandle.Alloc(bo2);
    unsafe{
        
    int* i1 = (int*)(((IntPtr)h1).ToPointer());
        
    int* i2 = (int*)(((IntPtr)h2).ToPointer());
                    
        
    int off1 = *((int*)((new IntPtr(*i1+4)).ToPointer()));
        
    int off2 = *((int*)((new IntPtr(*i2+4)).ToPointer()));
        Console.WriteLine(
    "Size is:"+(off2-off1).ToString());
    }

    上面代码中,其实就是指针->指针->偏移+4这么一个转化。这个值,我是从debug-memory里面,“猜测”出来的,呵呵。没有仔细分析过clr header,所以我不知道它的原理。
    好了,用上面的方法,我们得到的大小是:Size is:1000032。
    我相信,这应该是一个中规中矩的销售订单的占用内存大小。

    欢迎各位高手指教!
  • 相关阅读:
    AtCoder Grand Contest 019
    upd 2020.10.31
    ubuntu 自动配置脚本
    linux下gcc、g++不同版本的安装和切换
    fixes for 100% disk usage on Windows 10
    简单聊聊VisualStudio的断点调试
    运算符重载
    设计模式之桥接模式
    使用C#进行数据库增删改查ADO.NET(三)
    使用C#进行数据库增删改查ADO.NET(二)
  • 原文地址:https://www.cnblogs.com/juqiang/p/217141.html
Copyright © 2020-2023  润新知