6个重要的.NET概念: - 堆栈,堆,值类型,引用类型,装箱和拆箱(转)
今天在Code Project上面看到一篇文章《6 important .NET concepts: - Stack, heap, Value types, reference types, boxing and Unboxing》,觉得对初学.NET的朋友很有帮助。随手翻译,如有错误欢迎指正和讨论。
(以下是文章作者 Shivprasad koirala的简介和广告)
Watch my 500 videos on various topics like design patterns,WCF, WWF , WPF, LINQ ,Silverlight,UML, Sharepoint ,Azure,VSTS and lot more click here , you can also catch me on my trainings @ click here.
简介:
本文将介绍6个重要的概念,分别是堆栈,堆,值类型,引用类型,装箱和拆箱。首先会简单的解释一下当声明了一个变量的时候,程序的背后会发生什么;之后会介绍堆栈(Stack)和堆(Heap)的概念,然后围绕值类型(Value Type)和引用类型(Reference Type)进行一些探讨。
文章的最后一部分通过一个例子来说明装箱(Boxing)和拆箱(Unboxing)对程序性能方面的影响。
(图片引用自http://michaelbungartz.wordpress.com/)
声明变量时发生了什么?
当你在.NET程序中声明了一个变量的时候,它将会为你在内存中申请一段存储空间,这部分存储包括3个部分: 1.变量名称 2. 变量的类型 3.变量的值
对于不同数据类型,.NET中的变量可能会被分配在堆栈或堆之上,下面我们将会详细的讨论这两种不同的存储类型。
堆栈和堆
我们来分析下面的代码:

;
//Line 3
class1 cls1 = new class1();
}

Line1: 当执行这一段代码的时候,编译器将会在被称作"堆栈"的存储空间中分配一部分内存用于存储变量i。栈同时将会负责监控这部分内存的使用情况。
Line2:程序继续执行到这部分代码。正如"栈"的名称所表示的那样,程序将在刚才分配的空间的“栈顶”上再分配一部分内存用于存储变量j。
可以假想“堆栈”就是许多的的箱子,这些箱子一个接一个的叠放成一摞。
堆栈上存储空间的分配和释放遵循先进后出(LIFO)的原则,也就是说存储空间的分配和释放操作只能在堆栈的一端进行。(其实就是只能在栈顶进行)
Line3:我们在第三行创建了一个对象(Object)。当执行到这段代码的时候,编译器将在堆栈上创建一个指针,而实际的对象将被存储在被称为“堆”的存储空间。“堆”不像“堆栈”那样监控内存的使用情况,它仅仅是将其中的对象排列起来,这部分内存在任何情况下都可以被程序访问。堆用于动态的内存分配。
这里需要指出的一点是:cls1是被分配在堆栈上的。如果仅仅是声明一个对象,例如
{
对于Class1 cls1; 堆上并没有创建任何的存储空间用于存储Class1的对象,它仅在堆栈上创建以一个变量cls1(指向为空)。当执行到new关键字的时候,程序才会在堆上分配相应的内存。
退出方法时:当程序执行到方法的结尾时,将会释放掉该方法在堆栈上分配的空间。换句话说,int类型的变量将按照先进后出的原则被释放出堆栈空间。
这时堆上分配的内存并没有得到释放,这部分内存将会在之后被垃圾收集器收集并最终释放。
有人可能会有疑问,为什么要有两种存储空间类型,难道不能将所有的变量都分配到同一类型中去么?
仔细研究后你会发现,原生数据类型(primitive data type)往往比较简单,他们所包含的数据也很单一,比如“int i = 0”。 而对象数据类型(object data type)往往比较复杂,在对象数据类型中可能会包含其他的对象数据类型和原生数据类型。也就是说对象数据类型中含有多个“内容”而且这些“内容”都必须被存储在内存中。所以对象数据类型需要动态存储空间,而原生数据类型需要静态存储空间。动态存储空间将被分配在堆上而静态存储空间则分配在堆栈上。
值类型和引用类型
当简单了解了堆栈和栈之后,我们来看看值类型和引用类型的概念。
值类型将其所包含的值和其所在的地址存储在一起,而引用类型只包含一个指向其存储地址的指针。
下面的例子中我们将整形变量i的值赋给另一个整形变量j,i和j都的值将被分配到堆栈上。
当我们将一个int变量值赋给另一个int变量时,将会创建一个完全不同的拷贝。也就是说当其中一个值发生变化后,另一个值并不会受到影响。这种数据类型被称作是“值类型”。
当我们创建了一个对象并将该对象赋值给其他对象时,两者都将指向同一内存地址。如下图所示,obj和obj1指向堆上的同一地址。
在这种情况下,当其中一个对象的值被修改之后,另一个对象也会受到影响。我们称这种类型为“引用类型”。
我们有哪些值类型和引用类型?
在.NET中,变量被分配在堆栈上或是分配在堆上是由变量的类型决定的。 “String”和“Objects”是引用类型,而其他的原生类型则将被分配在堆栈上(值类型)。如图所示:
装箱与拆箱
好了,我们已经了解了这么多的相关的知识,那么它们在实际的编程当中有什么用呢?我们可以用它们来帮助理解数据在堆栈和堆之间的转移所带来的性能方面的影响。
来看看下图中的例子。当我们将值类型转换为引用类型时,数据从堆栈上转移到堆上。反过来,将引用类型转换为值类型时,数据从堆转移至堆栈。这种从堆和堆栈的数据转移,将对程序的性能产生不利的影响。
值类型转换为引用类型,我们称之为“装箱”(Boxing),反之,称之为“拆箱”(Unboxing)。
用ILDASM 反编译上面的代码,可以通过中间代码(IL)了解到装箱和拆箱操作,如下图所示:
装箱和拆箱的对程序造成的性能影响
我们可以分别执行下列的两个方法各10000次,第一个方法包含装箱的操作,而另一个比较简单。
含有装箱操作的方法需要执行3542毫秒,而另一方法执行了2477毫秒,所以,在实际应用当中,应该尽量避免出现装箱和拆箱的操作,仅在必需的情况下,再去使用它们。
源代码
本文中出现的装箱和拆箱操作所造成的性能影响的对比示例。
2011/4/14修改:大家看完本文后如果想更深入的了解.NET中堆和栈的关系,推荐这篇文章http://www.cnblogs.com/c2303191/articles/1065675.html
6个重要的.NET概念: - 堆栈,堆,值类型,引用类型,装箱和拆箱(转)的更多相关文章
- 6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱
引言 本篇文章主要介绍.NET中6个重要的概念:栈,堆,值类型,引用类型,装箱,拆箱.文章开始介绍当你声明一个变量时,编译器内部发生了什么,然后介绍两个重要的概念:栈和堆:最后介绍值类型和引用类型,并 ...
- [No0000136]6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱
引言 本篇文章主要介绍.NET中6个重要的概念:栈,堆,值类型,引用类型,装箱,拆箱.文章开始介绍当你声明一个变量时,编译器内部发生了什么,然后介绍两个重要的概念:栈和堆:最后介绍值类型和引用类型,并 ...
- C# 托管与非托管类型 堆和栈 值类型与引用类型 装箱与拆箱
一.托管类型与非托管类型 1.托管类型 托管类型包括 引用类型 以及 包含有引用类型或托管类型成员的结构. 引用类型 含引用类型或托管类型成员(字段.自动实现 get 访问器的属性)的结构 // 托管 ...
- C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)
前言 之前对几个没什么理解,只是简单的用过可空类型,也是知道怎么用,至于为什么,还真不太清楚,通过整理本文章学到了很多知识,也许对于以后的各种代码优化都有好处. 本文的重点就是:值类型直接存储其值,引 ...
- .NET六大剑客:栈、堆、值类型、引用类型、装箱和拆箱
.NET六大剑客:栈.堆.值类型.引用类型.装箱和拆箱 一.“堆”,“栈”专区 这两个字我相信大家太熟悉了,甚至于米饭是什么?不知道...“堆”,“栈”是什么?哦,这个知道... 之前我也写过一篇堆栈 ...
- 深入C#内存管理来分析值类型&引用类型,装箱&拆箱,堆栈几个概念组合之间的区别
C#初学者经常被问的几道辨析题,值类型与引用类型,装箱与拆箱,堆栈,这几个概念组合之间区别,看完此篇应该可以解惑. 俗话说,用思想编程的是文艺程序猿,用经验编程的是普通程序猿,用复制粘贴编程的是2B程 ...
- 【译】.NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱
为何要翻译 一来是为了感受国外优秀技术社区知名博主的高质量文章,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果,最后也是为了锻炼一下自己的英文读写能力.因为是首次翻译英文文章(哎,原谅我这个 ...
- .NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱 (转)
作者: Edison Chou 来源: 博客园 发布时间: 2014-09-03 15:59 阅读: 318 次 推荐: 2 原文链接 [收藏] 原文作者:Shivprasad k ...
- .NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱
为何要翻译 一来是为了感受国外优秀技术社区知名博主的高质量文章,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果,最后也是为了锻炼一下自己的英文读写能力.因为是首次翻译英文文章(哎,原谅我这个 ...
随机推荐
- Struts2 中添加 Servlet
Struts2中如何添加Servlet 以前Java开发都是Servlet的天下,如今是各种框架横行,遇到一个需要将以前的Servlet加入到现有的Struts2的环境中. Google之后发现Sta ...
- javascript同步分页
目前网上分页的例子比较多,但是对其原理不是很了解,平时用的时候只是拿来调用,今天花了点时间,采用面向对象方式写了一个demo.对其方法做了封装,对外只提供一个调用接口. window.loadPage ...
- Linux-PATH_环境变量
PATH变量 是linux系统里的一个环境变量,系统已经定义好了,我们不需要再定义. 作用: 是linux里使用的命令都存在在PATH变量后面指定的目录下,我们使用命令 ...
- 通过修改注册表设置windows环境变量
开发环境搭建每次都要设置很多环境变量, 一般是通过 [菜单]->[计算机]->[属性]->[高级设置]->[环境变量]进行设置,重装系统后,每次都要设置很多环境变量,很麻烦. ...
- OpenCMS模板的导出和OpenCMS网站的导出
1.OpenCMS模板的导出 (1)切换到Administration视图,单击Module Management,如图所示: (2)导出位置:tomcat根目录\webapps\opencms\ ...
- CSS布局(四) float详解
一.float设计初衷 因为float被设计出来的初衷是用于--文字环绕效果.即,一个图片一段文字,图片float:left之后,文字会环绕图片. <div style="width: ...
- hihoCoder1330 数组重排
题意 小Hi想知道,如果他每次都按照一种固定的顺序重排数组,那么最少经过几次重排之后数组会恢复初始的顺序? 具体来讲,给定一个1 - N 的排列 P,小Hi每次重排都是把第 i 个元素放到第 Pi个位 ...
- hdu1800 贪心+hash+真的有毒
这道题用map<string,int>TLE到死.这题又是一道毒题,看了评论,居然可以用int读入,而且网上还有用排序的....用int的连前导0都不需要处理了 说下贪心吧,每把扫帚一定要 ...
- [Essay] Apache Flink:十分可靠,一分不差
Apache Flink:十分可靠,一分不差 Apache Flink 的提出背景 我们先从较高的抽象层次上总结当前数据处理方面主要遇到的数据集类型(types of datasets)以及在处理数据 ...
- 使用phpstorm提交svn代码版本管理系统遇到的问题解决办法
1.当自己提交代码的时候显示out of date的时候,表示我们本地的代码过时啦,需要更新一下再提交. 即:更新一下再提交即可. 2.当自己的代码和服务器上的冲突的时候,我们右键点击冲突的文件,选择 ...