如何计算托管对象的大小

想知道这个,原因就一个: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。
我相信,这应该是一个中规中矩的销售订单的占用内存大小。

欢迎各位高手指教!
posted @ 2005-08-17 22:03  鞠强  阅读(5436)  评论(20编辑  收藏  举报

hello

world