查了不少资料,李建忠老师的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 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 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;
}
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;
}
// 这里为了测试,只是简单的用了几个类型
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());
}
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。
我相信,这应该是一个中规中矩的销售订单的占用内存大小。
欢迎各位高手指教!