浅谈C#中堆和栈的区别(附上图解)_第1页
浅谈C#中堆和栈的区别(附上图解)_第2页
浅谈C#中堆和栈的区别(附上图解)_第3页
浅谈C#中堆和栈的区别(附上图解)_第4页
浅谈C#中堆和栈的区别(附上图解)_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、浅谈C#中堆和栈的区别(附上图解)C#中栈是编译期间就分配好的内存空间,因此你的代码中必须就栈的大小有明确的定义;堆是程序运行期间动态分配的内存空间,你可以根据程序的运行情况确定要分配的堆内存的大小线程堆栈:简称栈 Stack托管堆: 简称堆 Heap使用.Net框架开发程序的时候,我们无需关心内存分配问题,因为有GC这个大管家给我们料理一切。如果我们写出如下两段代码:代码段1:?123456public int AddFive(int pValue) int result; result = pValue + 5; return result; 代码段2:?1234567891011publ

2、ic class MyInt   public int MyValue;   public MyInt AddFive(int pValue) MyInt result = new MyInt(); result.MyValue = pValue + 5; return result; 问题1:你知道代码段1在执行的时候,pValue和result在内存中是如何存放,生命周期又如何?代码段2呢?要想释疑以上问题,我们就应该对.Net下的栈(Stack)和托管堆(Heap)(简称堆)有个清楚认识,本立而道生。如果你想提高程序性能,理解栈和堆,必须的!本文就从栈和堆,

3、类型变量展开,对我们写的程序进行庖丁解牛。C#程序在CLR上运行的时候,内存从逻辑上划分两大块:栈,堆。这俩基本元素组成我们C#程序的运行环境。一,栈 vs 堆:区别?栈通常保存着我们代码执行的步骤,如在代码段1中 AddFive()方法,int pValue变量,int result变量等等。而堆上存放的则多是对象,数据等。(译者注:忽略编译器优化)我们可以把栈想象成一个接着一个叠放在一起的盒子。当我们使用的时候,每次从最顶部取走一个盒子。栈也是如此,当一个方法(或类型)被调用完成的时候,就从栈顶取走(called a Frame,译注:调用帧),接着下一个。堆则不然,像是一个仓库,储存着我

4、们使用的各种对象等信息,跟栈不同的是他们被调用完毕不会立即被清理掉。如图1,栈与堆示意图(图1)栈内存无需我们管理,也不受GC管理。当栈顶元素使用完毕,立马释放。而堆则需要GC(Garbage collection:垃圾收集器)清理。二,什么元素被分配到栈?什么被分配到堆?当我们程序执行的时候,在栈和堆中分配有四种主要的类型:值类型,引用类型,指针,指令。值类型:在C#中,继承自System.ValueType的类型被称为值类型,主要有以下几种(CLR2.0中支持类型有增加):* bool* byte* char* decimal* double* enum* float* int* long

5、* sbyte* short* struct* uint* ulong* ushort引用类型:以下是引用类型,继承自System.Object:* class* interface* delegate* object* string指针:在内存区中,指向一个类型的引用,通常被称为“指针”,它是受CLR( Common Language Runtime:公共语言运行时)管理,我们不能显示使用。需要注意的是,一个类型的引用即指针跟引用类型是两个完全不同的概念。指针在内存中占一块内存区,它本身只代表一个内存地址(或者null),它所指向的另一块内存区才是我们真正的数据或者类型。如图2:(图2)指令

6、:后文对指令再做介绍。三,如何分配?我们先看一下两个观点:观点1,引用类型总是被分配在堆上。(正确?)观点2,值类型和指针总是分配在被定义的地方,他们不一定被分配到栈上。(这个理解起来有点难度,需要慢慢来)上文提及的栈(Stack),在程序运行的时候,每个线程(Thread)都会维护一个自己的专属线程堆栈。当一个方法被调用的时候,主线程开始在所属程序集的元数据中,查找被调用方法,然后通过JIT即时编译并把结果(一般是本地CPU指令)放在栈顶。CPU通过总线从栈顶取指令,驱动程序以执行下去。下面我们以实例来详谈。还是我们开篇所列的代码段1:?123456public int AddFive(in

7、t pValue) int result; result = pValue + 5; return result; 当AddFive方法开始执行的时候,方法参数(parameters)则在栈上分配。如图3:(图3)注意:方法并不在栈中存活,图示仅供参考。接着,指令指向AddFive方法内部,如果该方法是第一次执行,首先要进行JIT即时编译。如图4:(图4)当方法内部开始执行的时候,变量result被分配在栈上,如图5:(图5)方法执行完毕,而且方法返回后,如图6所示:(图6)在方法执行完毕返回后,栈上的区域被清理。如图7:(图7)以上看出,一个值类型变量,一般会分配在栈上。那观点2中所述又做何

8、理解?“值类型和指针总是分配在被定义的地方,他们不一定被分配到栈上”。原因就是如果一个值类型被声明在一个方法体外并且在一个引用类型中,那它就会在堆上进行分配。还是代码段2:?1234567891011public class MyInt   public int MyValue;   public MyInt AddFive(int pValue) MyInt result = new MyInt(); result.MyValue = pValue + 5; return result; 当线程开始执行AddFive方法的时候,参数被分配到栈上,如图8所示:(图

9、8)由于MyInt是一个引用类型,所以它被分配到堆上,并且在栈中生成一个指针(result),如图9:(图9)AddFive方法执行完毕时的情况如图10:(图10)栈上内存被清理,堆中依然存在,如图11:(图11)当程序需要更多的堆空间时,GC需要进行垃圾清理工作,暂停所有线程,找出所有不可达到对象,即无被引用的对象,进行清理。并通知栈中的指针重新指向地址排序后的对象。现在我们应该知道,了解栈和堆,对我们开发出高性能程序的重要性。当我们使用引用类型的时候,一般是对指针进行的操作而非引用类型对象本身。但是值类型则操作其本身。接下来,我们用例子说明这一点。例1:?123456789public i

10、nt ReturnValue() int x = new int(); x = 3; int y = new int(); y = x;  y = 4;  return x; 执行结果为3,稍作修改:例2:?1234567891011121314public class MyInt public int MyValue;   public int ReturnValue2() MyInt x = new MyInt(); x.MyValue = 3; MyInt y = new MyInt(); y = x;  y.MyValue = 4;  return x.MyValue; 执行结果为4。我们来分析下原因,其实例1的跟以下代码所起效用一样:?1234567public int ReturnValue() int x = 3; int y = x;  y = 4; return x; 如图12所示,在栈上x和y分别占用一块内存区,互不干扰。(图12)而例2,与以下代

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论