.NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱

内容导读

•概述

•当你声明一个变量背后发生了什么?
•堆和栈
值类型引用类型
•哪些是值类型,哪些是引用类型?
•装箱和拆箱
•装箱和拆箱的性能问题
一、概述
本文会阐述六个重要的概念:堆、栈、值类型、引用类型、装箱和拆箱。本文首先会通过阐述当你定义一个变量之后系统内部发生的改变开始讲解,然后将关注点转移到存储双雄:堆和栈。之后,我们会探讨一下值类型和引用类型,并对有关于这两种类型的重要基础内容做一个讲解。
本文会通过一个简单的代码来展示在装箱和拆箱过程中所带来的性能上的影响,请各位仔细阅读。

.NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱

二、当你声明一个变量背后发生了什么?
当你在一个.NET应用程序中定义一个变量时,在RAM中会为其分配一些内存块。这块内存有三样东西:变量的名称、变量的数据类型以及变量的值。
上面简单阐述了内存中发生的事情,但是你的变量究竟会被分配到哪种类型的内存取决于数据类型。在.NET中有两种可分配的内存:栈和堆。在接下来的几个部分中,我们会试着详细地来理解这两种类型的存储。

.NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱

三、存储双雄:堆和栈

为了理解栈和堆,让我们通过以下的代码来了解背后到底发生了什么。

public void Method1(){// Line 1int i=4;// Line 2int y=2;//Line 3class1 cls1 = new class1();}

代码只有三行,现在我们可以一行一行地来了解到底内部是怎么来执行的。

•Line 1:当这一行被执行后,编译器会在栈上分配一小块内存。栈会在负责跟踪你的应用程序中是否有运行内存需要

•Line 2:现在将会执行第二步。正如栈的名字一样,它会将此处的一小块内存分配叠加在刚刚第一步的内存分配的顶部。你可以认为栈就是一个一个叠加起来的房间或盒子。在栈中,数据的分配和解除都会通过LIFO (Last In First Out)即先进后出的逻辑规则进行。换句话说,也就是最先进入栈中的数据项有可能最后才会出栈。

•Line 3:在第三行中,我们创建了一个对象。当这一行被执行后,.NET会在栈中创建一个指针,而实际的对象将会存储到一个叫做“堆”的内存区域中。“堆”不会监测运行内存,它只是能够被随时访问到的一堆对象而已。不同于栈,堆用于动态内存的分配。

•这里需要注意的另一个重要的点是对象的引用指针是分配在栈上的。 例如:声明语句 Class1 cls1; 其实并没有为Class1的实例分配内存,它只是在栈上为变量cls1创建了一个引用指针(并且将其默认职位null)。只有当其遇到new关键字时,它才会在堆上为对象分配内存。

•离开这个Method1方法时(the fun):现在执行控制语句开始离开方法体,这时所有在栈上为变量所分配的内存空间都会被清除。换句话说,在上面的示例中所有与int类型相关的变量将会按照“LIFO”后进先出的方式从栈中一个一个地出栈。

•需要注意的是:这时它并不会释放堆中的内存块,堆中的内存块将会由垃圾回收器稍候进行清理。

.NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱

现在我们许多的开发者朋友一定很好奇为什么会有两种不同类型的存储?我们为什么不能将所有的内存块分配只到一种类型的存储上?

如果你观察足够仔细,基元数据类型并不复杂,他们仅仅保存像 ‘int i = 0’这样的值。对象数据类型就复杂了,他们引用其他对象或其他基元数据类型。换句话说,他们保存其他多个值的引用并且这些值必须一一地存储在内存中。对象类型需要的是动态内存而基元类型需要静态内存。如果需求是动态内存的话,那么它将会在堆上为其分配内存,相反,则会在栈上为其分配。

.NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱

四、值类型和引用类型
既然我们已经了解了栈和堆的概念了,是时候了解值类型和引用类型的概念了。值类型将数据和内存都保存在同一位置,而一个引用类型则会有一个指向实际内存区域的指针。
通过下图,我们可以看到一个名为i的整形数据类型,它的值被赋值到另一个名为j的整形数据类型。他们的值都被存储到了栈上。
当我们将一个int类型的值赋值到另一个int类型的值时,它实际上是创建了一个完全不同的副本。换句话说,如果你改变了其中某一个的值,另一个不会发生改变。于是,这些种类的数据类型被称为“值类型”。

.NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱

当我们创建一个对象并且将此对象赋值给另外一个对象时,他们彼此都指向了如下图代码段所示的内存中同一块区域。因此,当我们将obj赋值给obj1时,他们都指向了堆中的同一块区域。换句话说,如果此时我们改变了其中任何一个,另一个都会受到影响,这也说明了他们为何被称为“引用类型”。
五、哪些是值类型,哪些是引用类型?
在.NET中,变量是存储到栈还是堆中完全取决于其所属的数据类型。比如:‘String’或‘Object’属于引用类型,而其他.NET基元数据类型则会被分配到栈上。下图则详细地展示了在.NET预置类型中,哪些是值类型,哪些又是引用类型。

.NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱

六、装箱和拆箱
现在,你已经有了不少的理论基础了。现在,是时候了解上面的知识在实际编程中的使用了。在应用中最大的一个意义就在于:理解数据从栈移动到堆的过程中所发生的性能消耗问题,反之亦然。
考虑一下以下的代码片段,当我们将一个值类型转换为引用类型,数据将会从栈移动到堆中。相反,当我们将一个引用类型转换为值类型时,数据也会从堆移动到栈中。
不管是在从栈移动到堆还是从堆中移动到栈上都会不可避免地对系统性能产生一些影响。
于是,两个新名词横空出世:当数据从值类型转换为引用类型的过程被称为“装箱”,而从引用类型转换为值类型的过程则被成为“拆箱”。

.NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱

如果你编译一下上面这段代码并且在ILDASM(一个IL的反编译工具)中对其进行查看,你会发现在IL代码中,装箱和拆箱是什么样子的。下图则展示了示例代码被编译后所产生的IL代码。

.NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱

七、装箱和拆箱的性能问题
为了弄明白到底装箱和拆箱会带来怎样的性能影响,我们分别循环运行10000次下图所示的两个函数方法。其中第一个方法中有装箱操作,另一个则没有。我们使用一个Stopwatch对象来监视时间的消耗。
具有装箱操作的方法花费了3542毫秒来执行完成,而没有装箱操作的方法只花费了2477毫秒,整整相差了1秒多。而且,这个值也会因为循环次数的增加而增加。也就是说,我们要尽量避免装箱和拆箱操作。在一个项目中,如果你需要装箱和装箱,请仔细考虑它是否是绝对必不可少的操作,如果不是,那么尽量不用。

.NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱

虽然以上代码段没有展示拆箱操作,但其效果同样适用于拆箱。你可以通过写代码来实现拆箱,并且通过Stopwatch来测试其时间消耗。

 以上就是.NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱 的内容,更多相关内容请关注PHP中文网(www.php.cn)!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1432263.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月17日 06:26:03
下一篇 2025年12月17日 06:26:19

相关推荐

  • .Net 垃圾回收和大对象处理

    英文原文:Maoni Stephens,编译:赵玉开(@玉开Sir) CLR垃圾回收器根据所占空间大小划分对象。大对象和小对象的处理方式有很大区别。比如内存碎片整理 —— 在内存中移动大对象的成本是昂贵的,让我们研究一下垃圾回收器是如何处理大对象的,大对象对程序性能有哪些潜在的影响。 大对象堆和垃圾…

    2025年12月17日 好文分享
    000
  • C# 中 10 个你真的应该学习(和使用!)的功能

    如果你开始探索c#或决定扩展你的知识,那么你应该学习这些有用的语言功能,这样做有助于简化代码,避免错误,节省大量的时间。    1)async / await 使用async / await-pattern允许在执行阻塞操作时解除UI /当前线程的阻塞。async / await-pattern的工…

    好文分享 2025年12月17日
    000
  • 关于.NET异常处理的思考(上)

    在项目开发中,对于系统和代码的稳定性和容错性都是有对应的要求。实际开发项目中的代码与样例代码的区别,更多的是在代码的运行的稳定性、容错性、扩展性的比较。 因为对于实现一个功能来说,实现功能的核心代码是一样的,可能只是在写法上优化而已,但是在实现某一个操作上使用的类来说,这一点是绝大多数时候是一样的。…

    好文分享 2025年12月17日
    000
  • .NET异常设计原则

    异常是使用.net时必然会遇到的问题,但是,有太多的开发人员没有从api设计的角度考虑这个问题。在大部分工作中,他们自始至终都知道需要捕获什么异常以及哪些异常需要写入全局日志。如果你设计了可以让你正确使用异常的api,则可以显著减少修复缺陷的时间。 谁的错? 异常设计背后的基本理论始于这样一个问题,…

    好文分享 2025年12月17日
    000
  • .NET编程之线程池内幕

    本文通过对.net4.5的threadpool源码的分析讲解揭示.net线程池的内幕,并总结threadpool设计的好与不足。 线程池的作用 线程池,顾名思义,线程对象池。Task和TPL都有用到线程池,所以了解线程池的内幕有助于你写出更好的程序。由于篇幅有限,在这里我只讲解以下核心 概念: 线程…

    好文分享 2025年12月17日
    000
  • .NET下几个服务框架介绍

    简介 在公司的服务多了以后,为了调用上的方便,同时为了以后的服务治理,一般都会使用一些服务框架,这里主要介绍我知道的几个服务框架,简析一下这些服务框架的基本概念。 可投入生产环境使用的 以下两个服务框架,我已经见过有公司投入到生产环境,所以对于稳定性,应该不需要有太大的担心。    ServiceS…

    2025年12月17日
    000
  • .NET多线程编程—并发集合

    并发集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表、集合和数组都不是线程安全的,若无同步机制,他们不适合于接受并发的指令来添加和删除元素。 在并发代码中使用上述经典集合需要复杂…

    2025年12月17日
    000
  • .NET编程中Word/Excel 在线预览

    前言 近日项目中做到一个功能,需要上传附件后能够在线预览。之前也没做过这类似的,于是乎就查找了相关资料,.net实现office文件预览大概有这几种方式: 使用Microsoft的Office组件将文件直接转换为html文件(优点:代码实现最简单,工作强度最小。缺点:效果极差) 使用Microsof…

    2025年12月17日
    000
  • 记一次.NET代码重构(上)

    需求:是这样的,要开发一个短信发送的模板,不同客户可能会使用不同的模板,而不同的客户使用的变量参数也是不同的。 之前为了应急,线上已经完成了一个短信模板发送短信的功能,短信模板表也创建了,而且在表中已经新增了一条记录。我只需要做一个短信模板的增删改查界面就可以了,看上去我的任务挺简单的,老司机应该知…

    2025年12月17日 好文分享
    000
  • 记一次.NET代码重构(下)

    public override bool TryGetMember(GetMemberBinder binder, out object result){ if (!_dictionary.TryGetValue(binder.Name, out result)) { result = null; …

    2025年12月17日
    000
  • 为 Jenkins 配置 .NET 持续集成环境

    去年年底,得益于公司引入 jenkins,让我们在持续集成方面迈出了第一步,本文不赘述如何安装 jenkins,主要关注点在于配置 .net 环境。另外本文是在 windows 环境下安装的 jenkins 进行操作。 一、安装环境 首先我们需要先准备几个安装包,将它们安装到 Windows 上: …

    2025年12月17日
    000
  • C#学习日记14—引用类型 之 object类

         先让我们了解下什么叫object类 Object类:        object类是所有类型的基类,所有类型都是由他派生而来,C#中所有的类都是直接或间接的从Sytem.Object类中继承,(可能有点迷糊,没关系,举个例子,如果Object类是树干,我们前面所学的所有类就是 树枝 或者 …

    2025年12月17日
    000
  • C#学习日记15—-引用类型 之 string类型用法总结

         c#中定义了一个基本的类string,专门用于对字符串的操作,同样这个类也是在.net框架结构的名字空间system中定义的,是system.string的别名。字符串的用法十分广泛,在类的定义中封装了许多内部的操作,我们只需要简单的利用就可以了。 创建一个string对象:       …

    好文分享 2025年12月17日
    000
  • C#学习日记19—-引用类型 之 委托(Delegate) 类型

        对于代表(委托也是它)delegate类型的意义,前面 引用类型前言 (点击查看)  中已经介绍过了,这里就不再罗嗦了,简言之,委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,说白了就是可以把方法当作参数传递。所有的委托(delegate)都派生自system…

    2025年12月17日
    000
  • C#学习日记12—数据类型 之 引用类型(前言)

     前面我们学习完了c#中简单的数据类型—-值类型,后面学习c#中另一个数据类型—-引用类型, “引用” 这个词在这里的意思是,该类型的变量不直接存储所包含的值,而是指向他所要存储的值,也就是说 引用类型储存的是变量的地址 。c#中的引用类型有4种:  …

    好文分享 2025年12月17日
    000
  • c#.net中const和readonly的区别

    (1) readonly和const都是用来标示常量的。(2) 初始化赋值不同。const修饰的常量必须在声明的同时赋值。例如: public class Class1 { public const int MaxValue = 10; //正确声明 public const MInValue; /…

    好文分享 2025年12月17日
    000
  • XML中如何生成XML报表模板_XML生成XML报表模板的方法与示例

    利用XSLT、编程语言或模板引擎可生成XML报表模板:1. XSLT将源XML转换为结构化报表;2. Python等语言通过DOM操作动态构建XML;3. Jinja2等模板引擎支持变量与逻辑控制,实现灵活输出。 在XML中生成XML报表模板,实际上是指利用XML的结构化特性设计一个可复用的数据模板…

    2025年12月17日
    000
  • XML中如何删除指定节点_XML删除指定节点的方法与技巧

    使用DOM、XPath、SAX/StAX或工具库可删除XML指定节点。DOM适合中小文件,通过removeChild()删除目标节点;XPath支持复杂条件精准定位;SAX/StAX流式处理适用于大文件;工具库如ElementTree提供简洁API。选择方法需考虑文件大小与性能需求。 在处理XML文…

    2025年12月17日
    000
  • XML中如何校验XML节点顺序_XML校验XML节点顺序的方法与技巧

    答案:使用XSD的xs:sequence可严格校验XML节点顺序,如FirstName→LastName→Age;若顺序错乱则校验失败。 在XML处理过程中,校验节点顺序是确保数据结构符合预期的重要环节。特别是在与外部系统交互、接口对接或数据导入导出时,严格的节点顺序可能影响解析结果或业务逻辑。虽然…

    2025年12月17日
    000
  • XML中如何处理空白节点_XML处理空白节点的操作步骤

    正确处理XML空白节点需根据解析器设置或编程逻辑过滤非重要空白。例如Java DOM可设setIgnoringElementContentWhitespace(true),Python可预处理移除,.NET可通过PreserveWhitespace=false控制,默认保留空白;也可通过DTD/XS…

    2025年12月17日
    000

发表回复

登录后才能评论
关注微信