装箱拆箱

简介: 装箱拆箱

装箱和拆箱


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

// 装箱
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里拿到的参数后就不用再担心内部再次装箱拆箱问题了。

相关文章
|
开发框架 .NET 编译器
C# 9.0中的静态匿名函数:引入static关键字的新用法
【1月更文挑战第15天】C# 9.0为匿名函数带来了一个新的修饰符static,允许开发者明确指定匿名函数不会捕获其包含作用域中的任何变量。这一特性增强了代码的性能和可读性,同时减少了因不小心捕获变量而导致的潜在错误。本文将详细探讨C# 9.0中静态匿名函数的语法、使用场景以及它们如何影响代码的性能和安全性。
|
8月前
|
人工智能 数据可视化 数据挖掘
AI竟能独立完成顶会论文!The AI Scientist-v2:开源端到端AI自主科研系统,自动探索科学假设生成论文
The AI Scientist-v2 是由 Sakana AI 等机构开发的端到端自主科研系统,通过树搜索算法与视觉语言模型反馈实现科学假设生成、实验执行及论文撰写全流程自动化,其生成论文已通过国际顶会同行评审。
577 34
AI竟能独立完成顶会论文!The AI Scientist-v2:开源端到端AI自主科研系统,自动探索科学假设生成论文
|
5月前
|
人工智能 运维 开发工具
10分钟无痛部署!字节Coze开源版喂饭教程
字节跳动开源AI智能体平台Coze(含Studio开发工具+Loop运维系统),仅需2核CPU/4GB内存即可本地运行,48小时GitHub星标破9000。本文提供10分钟极速部署指南,涵盖Docker配置、模型服务调优及Qwen模型切换实战,零成本实现商用级AI开发,彻底降低智能体创作门槛。
|
安全 Oracle Java
edge浏览器加载java插件
edge浏览器加载java插件
882 1
|
Java 数据库连接 mybatis
在Spring Boot应用中集成MyBatis与MyBatis-Plus
在Spring Boot应用中集成MyBatis与MyBatis-Plus
512 5
|
网络架构
小区搜索过程
小区搜索是终端通过同步信号块SSB与小区建立联系的过程,包括取得小区下行频率、时间同步、检测小区识别号CellID、通过解码广播信道BCH上的系统信息。下行同步包括频率、符号和帧同步。
500 0
小区搜索过程
|
机器学习/深度学习 算法 计算机视觉
基于YOLOv8深度学习的102种花卉智能识别系统【python源码+Pyqt5界面+数据集+训练代码】目标识别、深度学习实战
基于YOLOv8深度学习的102种花卉智能识别系统【python源码+Pyqt5界面+数据集+训练代码】目标识别、深度学习实战
|
数据安全/隐私保护
阿里云企业实名认证怎么操作-如何进行企业实名认证
阿里云企业实名认证共分为四种方式1、通过企业支付宝授权认证。2、通过企业法人支付宝授权认证。 3、通过企业法人扫脸认证。 4、通过企业银行打款方式认证。注意:如果您选择通过企业支付宝认证,您提供的支付宝的主体信息必须为该企业的信息。
12519 3
|
数据可视化 开发工具 Python
PyCharm与Anaconda超详细安装配置教程
PyCharm与Anaconda超详细安装配置教程
9842 1
|
弹性计算 自然语言处理 机器人
智能语音交互-语音识别介绍 | 学习笔记
简介:快速学习智能语音交互-语音识别介绍
1218 0
智能语音交互-语音识别介绍 | 学习笔记