装箱拆箱

简介: 装箱拆箱

装箱和拆箱


什么是装箱和拆箱。它其实很简单,把值类型实例转换为引用类型实例,就是装箱。相反,把引用类型实例转换为值类型实例,就是拆箱。

// 装箱
int a = 5;
object obj = a;
//拆箱
a = (int)obj;

值类型数据会直接存储变量


装箱,因为a 是值类型是直接有数据的变量,obj为引用类型是指针与内存是拆分开来的,把 a 赋值给 b 实际上就是 b 为自己创建了一个指针并指向了a的数据空间。


引用类型的变量会在声明时预留出内存地址,其为null, 当进行new创建实例的时候,就分配了堆上的内存空间,把空间地址保存给这个引用变量。


拆箱,相当于把 obj 指向的内存空间复制一份交给了a,因为 a 是值类型,它不允许指向某个内存空间只能靠复制数据来传递数据。


为何需要装箱


栈是本着先进后出的数据结构(LIFO)原则的存储机制,它是一段连续的内存,所以对栈数据的定位比较快速, 而堆则是随机分配的空间,


处理的数据比较多, 无论如何,


至少要两次定位。堆内存的创建和删除节点的时间复杂度是O(logn)。栈创建和删除的时间复杂度则是O(1),栈速度更快。


那么既然栈速度这么快,全部用栈不就好了。这又涉及到生命周期问题,由于栈中的生命周期是必须确定的,销毁时必须按次序销毁,从最后分配的块部分开始销毁,创建后什么时候销毁必须是一个定量,所以在分配和销毁时不灵活,基本都用于函数调用和递归调用中,这些生命周期比较确定的地方。相反堆内存可以存放生命周期不确定的内存块,满足当需要删除时再删除的需求,所以堆内存相对于全局类型的内存块更适合,分配和销毁更灵活。


当程序、逻辑或接口需要更加通用的时候才会需要装箱。比如调用一个含类型为object的参数的方法,该object可支持任意为型,以便通用。当你需要将一个值类型(如Int32)传入时,就需要装箱。又比如一个非泛型的容器为了保证通用,而将元素类型定义为object,当值类型数据加入容器时需要装箱。


装箱的内部操作


装箱: 根据相应的值类型在堆中分配一个值类型内存块,再将数据拷贝给它。按三步进行。


第一步:在堆内存中新分配一个内存块(大小为值类型实例大小加上一个方法表指针和一个SyncBlockIndex)。


第二步:将值类型的实例字段拷贝到新分配的内存块中。


第三步:返回内存堆中新分配对象的地址。这个地址就是一个指向对象的引用了。


拆箱 :更为简单点,先检查对象实例,确保它是给定值类型的一个装箱值,再将该值从实例复制到值类型变量的内存块中。


装箱、拆箱对执行效率有哪些影响,如何优化。


由于装箱、拆箱时生成的是全新的对象,不断得分配和销毁内存会不但大量消耗CPU,也同时增加了内存碎片,降低了性能。 那该如何做呢?


最需要我们做的就是减少装箱、拆箱的操作,在我们编程规范中要牢记这种比较浪费CPU的操作,在平时编程要特别注意。


整数、浮点数、布尔等数值型变量的变化手段很少,变不出什么花样来,主要靠加强规范减少装拆箱的情况来提高性能。Struct 有点不一样,它既是值类型,又可以像类一样继承,用途多转换的途径多可变的花样多,稍不留神花样就变成了麻烦,所以这里讲讲 Struct 变化后的优化方法。


1.Struct 通过重载函数来避免拆箱、装箱。 ToString(), GetType(), 如果 Struct 没有写重载ToString()和GetType()的方法,就会在 Struct 实例调用它们时先装箱再调用,导致内存块重新分配性能损耗,所以对于那些需要调用的引用方法,必须重载。


2.通过泛型来避免拆箱、装箱。 不要忘了 Struct 也是可以继承的,在不同的、相似的、父子关系的 Struct 之间可以用泛型来传递参数,这样就不用装箱后再传递了。


比如B,C继承A,就可以有这个泛型方法 void Test(T t) where T:A,以避免使用object引用类型形式传递参数。


3.通过继承统一的接口提前拆箱、装箱,避免多次重复拆箱、装箱。 很多时候拆装箱不可避免,那么我们就让多种 Struct 继承某个统一的接口,不同的 Struct 就可以有相同的接口。把 Struct 传递到其他方法里去时就相当于提前进行了装箱操作,在方法中得到的是引用类型的值,并且有它需要的接口,避免了在方法中重复多次的拆装箱操作。比如 Struct A 和 Struct B 都继承接口 I,我们调用的方法是 void Test(I i)。当调用Test方法时传进去的 Struct A 或 Struct B 的实例都相当于提前做了装箱操作,Test里拿到的参数后就不用再担心内部再次装箱拆箱问题了。

相关文章
|
存储 SQL JSON
【ELK】(四)Elasticsearch 聚合查询与多维度数据统计
【ELK】(四)Elasticsearch 聚合查询与多维度数据统计
1450 0
【ELK】(四)Elasticsearch 聚合查询与多维度数据统计
|
10月前
|
安全 网络安全 Windows
服务器的内网可以访问,外网却不能访问的问题
服务器的内网可以访问,外网却不能访问的问题
|
6月前
|
Java
SonarQube使用教程
SonarQube使用教程
282 0
|
12月前
Leetcode 111 二叉树最小深度
Leetcode 111 二叉树最小深度
106 0
|
12月前
|
前端开发 Python
Scrapy CSS选择器
Scrapy CSS选择器
167 0
|
12月前
|
Ubuntu 关系型数据库 MySQL
实用的MySql 密码初始化方法Ubuntu!
最近安装了linux系统用来熟悉操作,但是linux的各种安装配置网上有很多帖子,很多帖子的方法都是没什莫用的。看了20多个帖子,就这个亲测的方法只有这个有效。
239 0
|
12月前
|
数据采集 资源调度 并行计算
Python全局锁GIL,详细解读
Python全局锁GIL,详细解读
212 0
|
12月前
|
Shell Python
Mac配置环境变量填坑
Mac配置环境变量填坑
104 0
|
12月前
|
Shell
ADB 命令指南
ADB 命令指南
191 0
|
12月前
|
前端开发 数据库 数据安全/隐私保护
Django-View视图
Django-View视图
46 0