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技术的基础拾遗达到温故知新的效果,最后也是为了锻炼一下自己的英文读写能力.因为是首次翻译英文文章(哎,原谅我这个 ...
随机推荐
- [2-SAT]【学习笔记】【未完】
这种一看就很2的东西.... 参考资料: 由对称性解2-sat问题 2-sat解法浅析 $SAT$理论: $2-SAT$ 两种形式: $x \in \hat B$ $x \lor y(x,\ y \i ...
- xcode7中使用cocos2d-x3.8的webview控件
在XCode7中使用cocos2d-x 3.3以上版本的WebView控件时,碰到了编译错误 App Transport Security has blocked a cleartext HTTP ( ...
- C语言之prinf的用法
1. n换行字符 1).直接输出内容 printf("哈哈\n"); 2).带参数的输出 int i = 10 ; %d:输入控制符 printf ("%d\n" ...
- mongoDB高级查询$type4array使用解析
今天在使用mongoDB高级查询$type:符号 -- 4代指Array类型发现一个问题. $type符号: $type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果. 下面是mong ...
- Go学习笔记01-语言
1.1 变量 Go 是静态类型语言,不能在运行期改变变量类型.使用关键字 var 定义变量,自动初始化为零值.如果提供初始化值,可省略变量类型,由编译器自动推断. var x int var f fl ...
- jquery toggle 方法被废除的替代方法
今天使用 toggle 方法的时候,该方法一直不能生效. 原来jquery 的引入文件是1.9,该方法在1.8以上已被废除. 那么简单的切换状态,我们可使用if 语句进行代替 如下: 记录一开始设置隐 ...
- 关于UIButton嵌入到UIView点击无反应问题的解决方法和delegate的简单用法示例(转载)
做项目封装UIView的时候碰到的问题,没想到有个哥们儿还写成博客,特此收藏! 问题是这样的,几个界面用到同一个自定义返回按钮,于是就想着把这个按钮单独封装起来,添加一个UIView类,在里面自定义U ...
- 大数相加(类似杭电acm1002)
/*输入两个非常大的整数(完全超出了int.long的表示范围),这个整数的长度可能超过100位,计算并输出这两个数相加的结果.*/ //自己用题目所给的案例测试,输出是正确的,也能输出正确的结果,不 ...
- spring oauth2 ,spring security整合oauth2.0 JdbcTokenStore实现 解决url-pattern .do .action
参考以下两个文章: http://www.cnblogs.com/0201zcr/p/5328847.html http://wwwcomy.iteye.com/blog/2230265 web.xm ...
- Redis 设置开机启动
1. 将下列代码保存为文件redis, 置于 /etc/init.d 下面 ########################### # chkconfig: 2345 90 10 redis服务必须在 ...