代码改变世界

谈CLR的装箱与拆箱

2010-12-29 11:53 姜 萌@cnblogs 阅读(...) 评论(...) 编辑 收藏

CLR类型系统有两种主要类型—Reference Type和ValueType。前者是在托管堆中被分配内存并接受管理,后者则有两种形态--装箱与未装箱,对于装箱形态的值对象是在托管堆中,未装箱形态的值对象是在栈上分配。

CLR堆上对象和栈上对象的差异

每一个堆上对象都有两个额外的字段,一个是类型引用表的指针,用于实现多态,类似于C++的多态机制,另一个字段是SyncBlockIndex,用于实现CLR线程同步机制。栈上对象则无这两个字段。

装箱的过程

装箱:
在堆上开辟内存,包括2个额外字段+值对象大小。
将值对象复制过去
3.返回新分配对象的地址。

拆箱的过程

1.如果为null,跑出NullReferenceException
2.如果引用指向的不是一个期望对象的已装箱对象,跑出InvalidCastException。
3.得到堆上引用对象中未装箱对象的指针。

两者是反过程吗

对比上述装箱和拆箱的过程,可以看出两者并非是互为反操作,拆箱过程本身并不涉及内存操作,不会像装箱那样拷贝数据,但是拆箱之后通常也还是要将值从堆上进行拷贝的栈上的。

性能差异

了解了装箱和拆箱的操作,我们可以清楚的明白:装箱操作会导致数据在堆和栈上进行拷贝,频繁的装箱操作会性能损失。而相比而言拆箱过程对性能损耗还是比较小的。

发现代码中的装箱和拆箱操作

1)

Int32 a = 100;

object b = a;//(A)

a = (int)b;//(B)

(A)发生一次装箱操作

(B)发生一次拆箱操作。

2)

Point p = new Point(1,5);

Console.WriteLine(p.Clone());//(A)

var p2 = p as ICloneable;//(B)

var p3 = (Point)p2;//(C)

(A):Point重载了Clone()方法,所以这一步无需借助多态,p本身不会被装箱。但是要注意:Clone返回的是一个object,所以这一步会出现一个装箱操作。

(B):会产生装箱操作

(C):这一步会出现拆箱操作,并发生内存拷贝(从托管堆上拷贝到栈上)。

使用ILDASM查看IL代码

vs自带的tools里提供了一个ILDASM的工具,能够查看程序集的IL代码。

在vs的命令行环境下(command prompt)执行“ILDASM  /adv”,/adv参数能开启一些高级操作。打开一个未混效过的程序集,我们就能查看到其IL代码,相信您能从中发现更多有趣的东西:)

image image