CLR类型系统有两种主要类型—Reference Type和ValueType前者是在托管堆中被分配内存并接受管理后者则有两种形态装箱与未装箱对于装箱形态的值对象是在托管堆中未装箱形态的值对象是在栈上分配
CLR堆上对象和栈上对象的差异
每一个堆上对象都有两个额外的字段一个是类型引用表的指针用于实现多态类似于C++的多态机制另一个字段是SyncBlockIndex用于实现CLR线程同步机制栈上对象则无这两个字段
装箱的过程
装箱
在堆上开辟内存包括个额外字段+值对象大小
将值对象复制过去
返回新分配对象的地址
拆箱的过程
如果为null跑出NullReferenceException
如果引用指向的不是一个期望对象的已装箱对象跑出InvalidCastException
得到堆上引用对象中未装箱对象的指针
两者是反过程吗
对比上述装箱和拆箱的过程可以看出两者并非是互为反操作拆箱过程本身并不涉及内存操作不会像装箱那样拷贝数据但是拆箱之后通常也还是要将值从堆上进行拷贝的栈上的
性能差异
了解了装箱和拆箱的操作我们可以清楚的明白装箱操作会导致数据在堆和栈上进行拷贝频繁的装箱操作会性能损失而相比而言拆箱过程对性能损耗还是比较小的
发现代码中的装箱和拆箱操作
)
Int a = ;
object b = a;//(A)
a = (int)b;//(B)
(A)发生一次装箱操作
(B)发生一次拆箱操作
)
Point p = new Point();
ConsoleWriteLine(pClone());//(A)
var p = p as ICloneable;//(B)
var p = (Point)p;//(C)
(A)Point重载了Clone()方法所以这一步无需借助多态p本身不会被装箱但是要注意Clone返回的是一个object所以这一步会出现一个装箱操作
(B)会产生装箱操作
(C)这一步会出现拆箱操作并发生内存拷贝(从托管堆上拷贝到栈上)
使用ILDASM查看IL代码
vs自带的tools里提供了一个ILDASM的工具能够查看程序集的IL代码
在vs的命令行环境下(command prompt)执行ILDASM /adv/adv参数能开启一些高级操作打开一个未混效过的程序集我们就能查看到其IL代码相信您能从中发现更多有趣的东西)