相同类型的每个对象大小都是一样的吗?
2009-11-30 14:09 Jeffrey Zhao 阅读(5566) 评论(41) 编辑 收藏 举报快速回答:“相同(引用)类型的每个对象大小都是一样的吗?”其实个问题对于大多数情况下来说应该正确的,不过的确也有些类型受到CLR的特殊照顾,因而有那么些例外。我现在尝试使用一些简单的小实验来进行验证,当然它是不严谨的,只能算是一个简单尝试而已。
比如,我们有这么一个类型:
public class SomeClass { public int Field; }
然后编写这样的代码:
var c1 = new SomeClass(); var c2 = new SomeClass(); var c3 = new SomeClass(); var c4 = new SomeClass(); var c5 = new SomeClass(); GC.Collect(2); unsafe { fixed (int* ptr1 = &c1.Field, ptr2 = &c2.Field, ptr3 = &c3.Field, ptr4 = &c4.Field, ptr5 = &c5.Field) { Console.WriteLine("Size of c1: " + ((int)ptr2 - (int)ptr1)); Console.WriteLine("Size of c2: " + ((int)ptr3 - (int)ptr2)); Console.WriteLine("Size of c3: " + ((int)ptr4 - (int)ptr3)); Console.WriteLine("Size of c4: " + ((int)ptr5 - (int)ptr4)); } }
运行这段代码的结果是:
Size of c1: 12 Size of c2: 12 Size of c3: 12 Size of c4: 12
当然,如果我们要得出“每个SomeClass对象大小是12字节”,那么还必须有如下两个前提:
- 在堆中分配对象时是连续的。
- 相同类型的实例,内部字段地布局(或者说“顺序”)是相同的。
但是,有一个类型可能是个特例,那就是我们随处可见的String类型。为此,我们再写一段代码进行实验:
static void Main() { var c1 = new SomeClass(); var s10 = new String('a', 10); var c2 = new SomeClass(); var s20 = new String('a', 20); var c3 = new SomeClass(); var s30 = new String('a', 30); var c4 = new SomeClass(); var s40 = new String('a', 40); var c5 = new SomeClass(); GC.Collect(2); unsafe { fixed (int* ptr1 = &c1.Field, ptr2 = &c2.Field, ptr3 = &c3.Field, ptr4 = &c4.Field, ptr5 = &c5.Field) { Console.WriteLine("Size of s10: " + ((int)ptr2 - (int)ptr1 - 12)); Console.WriteLine("Size of s20: " + ((int)ptr3 - (int)ptr2 - 12)); Console.WriteLine("Size of s30: " + ((int)ptr4 - (int)ptr3 - 12)); Console.WriteLine("Size of s40: " + ((int)ptr5 - (int)ptr4 - 12)); } } DoSomething(s10, s20, s30, s40); Console.ReadLine(); } private static void DoSomething(params object[] args) { // nothing }
DoSomething的作用仅仅是为了避免s10-40几个字符串直接被当作垃圾而释放掉。上面这段代码中可以得出:
Size of s10: 40 Size of s20: 60 Size of s30: 80 Size of s40: 100
这个结果似乎是说:长度为10的字符串占40字节,长度为20的字符串占60字节……以此类推,长度为n的字符串占20 + 2n个字节(这也说明CLR中的字符是使用双字节的Unicode进行存储)。
真的是这样吗?事实上这个实验中并不能严格得出“不同长度String对象大小不同”,因为,万一String类型也只是直接创建了一个字符数组呢?这样,其实每个String对象的大小还是相同的,大小不同的只是字符数组而已。那么究竟事实是怎么样的呢?这只能靠WinDBG + SOS来一探究竟了,有空我再试试看。
而现在,我也只能靠猜的。我猜String对象是在自身内部包含了一长串字符,并非引用了一个字符数组,因为您看它的构造函数其实都是extern的,要靠外部调用的东东其中一定有猫腻:
[MethodImpl(MethodImplOptions.InternalCall)] public extern String(char[] value); [MethodImpl(MethodImplOptions.InternalCall), CLSCompliant(false)] public extern unsafe String(char* value); [MethodImpl(MethodImplOptions.InternalCall), CLSCompliant(false)] public extern unsafe String(sbyte* value); [MethodImpl(MethodImplOptions.InternalCall)] public extern String(char c, int count); [MethodImpl(MethodImplOptions.InternalCall), CLSCompliant(false)] public extern unsafe String(char* value, int startIndex, int length); [MethodImpl(MethodImplOptions.InternalCall), CLSCompliant(false)] public extern unsafe String(sbyte* value, int startIndex, int length); [MethodImpl(MethodImplOptions.InternalCall)] public extern String(char[] value, int startIndex, int length); [MethodImpl(MethodImplOptions.InternalCall), CLSCompliant(false)] public extern unsafe String(sbyte* value, int startIndex, int length, Encoding enc);
最后回到标题:“相同(引用)类型的每个对象大小都是一样的吗?”答案肯定是否定的咯。最简单的例子便是int数组,不同长度的int数组类型相同,但大小明显不一样。当然,无论什么类型的数组都这样。