什么是箱
箱是指托管堆,装箱指在托管堆中将栈上的值类型进行封装,生成一份该值类型对象的副本,并返回该副本的地址。
而拆箱是返回已装箱值类型在托管堆中的地址(严格意义来讲:拆箱是不包括值类型字段的拷贝的)
值类型和引用类型是在不同的堆和栈上的,箱的操作,涉及数据的拷贝和内存的分配,所以引用性能的消耗
关于装箱和拆箱的几个问题
1.什么是拆箱和装箱?
装箱就是值类型转换为引用类型,拆箱就是引用类型(被装箱的对象)转换为值类型。
2.什么是箱子?
就是引用类型对象。
3.箱子放在哪里?
托管堆上。
4.装箱和拆箱有什么性能影响?
装箱和拆箱都涉及到内存的分配和对象的创建,有较大的性能影响。
5.如何避免隐身装箱?
编码中,多使用泛型、显示装箱。
6.箱子的基本结构?
上面说了,箱子就是一个引用类型对象,因此她的结构,主要包含两部分:
- 值类型字段值;
- 引用类型的标准配置,引用对象的额外空间:TypeHandle和同步索引块,关于这两个概念在本系列后面的文章会深入探讨。
7.装箱的过程?
- 1.在堆中申请内存,内存大小为值类型的大小,再加上额外固定空间(引用类型的标配:TypeHandle和同步索引块);
- 2.将值类型的字段值(x=1023)拷贝新分配的内存中;
- 3.返回新引用对象的地址(给引用变量object o)
8.拆箱的过程?
- 1.检查实例对象(object o)是否有效,如是否为null,其装箱的类型与拆箱的类型(int)是否一致,如检测不合法,抛出异常;
- 2.指针返回,就是获取装箱对象(object o)中值类型字段值的地址;
- 3.字段拷贝,把装箱对象(object o)中值类型字段值拷贝到栈上,意思就是创建一个新的值类型变量来存储拆箱后的值;
box(装箱)消耗大
box在堆栈中创建一个新的对象,性能消耗大
int i = 123;
// Boxing copies the value of i into object o.
object o = i;
过程
1、分配内存:在托管堆中分配好内存,内存的大小是值类型的各字段需要的内存量加上托管堆的所有对象都有的两个额外成员类型对象指针和同步块索引所需要的内存量之和。
2、复制对象:将值类型的字段复制到新分配的内存中
3、返回地址:将已装箱的值类型对象的地址返回给引用类型的变量
unboxing(拆箱)
- 检查对象实例
- 将该值从实例复制到值类型变量中
int i = 123; // a value type
object o = i; // boxing
int j = (int)o; // unboxing
过程
1、检查对象:检查类型是否为null,如果为空则抛出异常
2、返回地址:在堆中找到要拆箱对象的地址
3、数据拷贝:把object中的值类型字段拷贝到栈中,创建一个新的值对象来保存拷贝的值。
如何避免装箱拆箱
使用重载方法,方法的参数提供不同的类型。
使用泛型,在.Net中有泛型集合,比如:ArrayList使用泛型集合:(List<T>) 来避免box,Hashtable对应着Dictionary<TKey, Tvalue>
如果在循环中会有某个值进行装箱,可以在循环外先装箱,避免在循环中装箱。
调用值类型的ToString()方法不会引起装箱。
文档资料
http://msdn.microsoft.com/zh-cn/library/yz2be5wk.aspx
http://msdn.microsoft.com/zh-cn/library/ms173196.aspx
详细解释:.NET面试题解析(02)-拆箱与装箱