装箱与拆箱
- 作用
一般说:将值类型转为引用类型为装箱,而将装箱的引用类型还原为值类型则是拆箱。
例如: ArrayList可以同时容纳不同类型的数据,其本质就是将所有类型都作为object类型来对待,那么添加和移除值类型数据时,不可避免会进行装箱和拆箱的操作。
public virtual int Add(object value);
public virtual void Remove(object obj); 装箱过程
- 在托管堆中为新的引用类型分配内存(包括原本值类型实例的内存大小加上类型对象指针和同步索引块所需内存)
- 将值类型字段数据拷贝到所分配的内存中。
- 返回新对象的地址引用。
过程中有分配内存与数据拷贝两个性能消耗较大的操作。
装箱过程通常会在IL代码中用box标记。拆箱过程
需要说明拆箱后的类型,只有装过箱才可以拆箱,拆箱时要注意只能转型为最初未装箱时的值类型。- 获取托管堆中属于值类型那部分字段的地址,也就是严格意义上的拆箱。
- 将已装箱的对象在托管堆上的值拷贝到线程栈中的值类型实例中。
拆箱实际是获取引用的过程,而复制过程则是紧随的后一步。
装箱过程通常会在IL代码中用unbox标记。副作用
执行装箱操作时不可避免的要在堆上申请内存空间,并将堆栈上的值类型数据复制到申请的堆内存空间上,因此消耗内存和cpu资源,过多申请内存会在当前堆可用内存不足时频繁触发GC,而导致必然的卡顿。- 解决方案
通常会使用泛型作为替代方案,例如多用List< T>而不是ArrayList。