C#中堆和栈的区别分析

转载 2015年07月08日 05:43:16

线程堆栈:简称栈 Stack
托管堆: 简称堆 Heap

使用.Net框架开发程序的时候,我们无需关心内存分配问题,因为有GC这个大管家给我们料理一切。如果我们写出如下两段代码:

复制代码
1 代码段1:
2 
3 public int AddFive(int pValue)
4 {
5 int result;
6 result = pValue + 5;
7 return result;
8 }
复制代码
复制代码
 1 代码段2:
 2 
 3 public class MyInt
 4 {
 5 public int MyValue;
 6 }
 7 
 8 public MyInt AddFive(int pValue)
 9 {
10 MyInt result = new MyInt();
11 result.MyValue = pValue + 5;
12 return result;
13 }
复制代码

问题1:你知道代码段1在执行的时候,pValue和result在内存中是如何存放,生命周期又如何?代码段2呢?
要想释疑以上问题,我们就应该对.Net下的栈(Stack)和托管堆(Heap)(简称堆)有个清楚认识,本立而道生。如果你想提高程序性能,理解栈和堆,必须的!
本文就从栈和堆,类型变量展开,对我们写的程序进行庖丁解牛。
C#程序在CLR上运行的时候,内存从逻辑上划分两大块:栈,堆。这俩基本元素组成我们C#程序的运行环境。

一,栈 vs 堆:区别?

栈通常保存着我们代码执行的步骤,如在代码段1中 AddFive()方法,int pValue变量,int result变量等等。而堆上存放的则多是对象,数据等。(译者注:忽略编译器优化)我们可以把栈想象成一个接着一个叠放在一起的盒子。当我们使用的时候,每次从最顶部取走一个盒子。栈也是如此,当一个方法(或类型)被调用完成的时候,就从栈顶取走(called a Frame,译注:调用帧),接着下一个。堆则不然,像是一个仓库,储存着我们使用的各种对象等信息,跟栈不同的是他们被调用完毕不会立即被清理掉

栈内存无需我们管理,也不受GC管理。当栈顶元素使用完毕,立马释放。而堆则需要GC(Garbage collection:垃圾收集器)清理。


二,什么元素被分配到栈?什么被分配到堆?

当我们程序执行的时候,在栈和堆中分配有四种主要的类型:值类型,引用类型,指针,指令。

值类型:
在C#中,继承自System.ValueType的类型被称为值类型,主要有以下几种(CLR2.0中支持类型有增加):

* bool
* byte
* char
* decimal
* double
* enum
* float
* int
* long
* sbyte
* short
* struct
* uint
* ulong
* ushort

引用类型:
以下是引用类型,继承自System.Object:
* class
* interface
* delegate
* object
* string

指针:
在内存区中,指向一个类型的引用,通常被称为“指针”,它是受CLR( Common Language Runtime:公共语言运行时)管理,我们不能显示使用。需要注意的是,一个类型的引用即指针跟引用类型是两个完全不同的概念。指针在内存中占一块内存区,它本身只代表一个内存地址(或者null),它所指向的另一块内存区才是我们真正的数据或者类型。

指令:
后文对指令再做介绍。

三,如何分配?
我们先看一下两个观点:
观点1,引用类型总是被分配在堆上。(正确?)
观点2,值类型和指针总是分配在被定义的地方,他们不一定被分配到栈上。(这个理解起来有点难度,需要慢慢来)

上文提及的栈(Stack),在程序运行的时候,每个线程(Thread)都会维护一个自己的专属线程堆栈。
当一个方法被调用的时候,主线程开始在所属程序集的元数据中,查找被调用方法,然后通过JIT即时编译并把结果(一般是本地CPU指令)放在栈顶。CPU通过总线从栈顶取指令,驱动程序以执行下去。

下面我们以实例来详谈。

还是我们开篇所列的代码段1:

复制代码
1 public int AddFive(int pValue)
2 {
3 int result;
4 result = pValue + 5;
5 return result;
6 }
复制代码

当AddFive方法开始执行的时候,方法参数(parameters)则在栈上分配。

接着,指令指向AddFive方法内部,如果该方法是第一次执行,首先要进行JIT即时编译。

当方法内部开始执行的时候,变量result被分配在栈上,方法执行完毕,而且方法返回后,栈上的区域被清理。

以上看出,一个值类型变量,一般会分配在栈上。那观点2中所述又做何理解?“值类型和指针总是分配在被定义的地方,他们不一定被分配到栈上”。
原因就是如果一个值类型被声明在一个方法体外并且在一个引用类型中,那它就会在堆上进行分配。
还是代码段2:

复制代码
 1 public class MyInt
 2 {
 3 public int MyValue;
 4 }
 5 
 6 public MyInt AddFive(int pValue)
 7 {
 8 MyInt result = new MyInt();
 9 result.MyValue = pValue + 5;
10 return result;
11 }
复制代码

当线程开始执行AddFive方法的时候,参数被分配到栈上,由于MyInt是一个引用类型,所以它被分配到堆上,并且在栈中生成一个指针(result),AddFive方法执行完毕时,栈上内存被清理,堆中依然存在。

    当程序需要更多的堆空间时,GC需要进行垃圾清理工作,暂停所有线程,找出所有不可达到对象,即无被引用的对象,进行清理。并通知栈中的指针重新指向地址排序后的对象。现在我们应该知道,了解栈和堆,对我们开发出高性能程序的重要性。当我们使用引用类型的时候,一般是对指针进行的操作而非引用类型对象本身。但是值类型则操作其本身。
接下来,我们用例子说明这一点。

复制代码
 1 例1:
 2 
 3 public int ReturnValue()
 4 {
 5 int x = new int();
 6 x = 3;
 7 int y = new int();
 8 y = x;
 9 y = 4;
10 return x;
11 }
复制代码

执行结果为3,稍作修改:

复制代码
 1 例2:
 2 
 3 public class MyInt
 4 {
 5 public int MyValue;
 6 }
 7 
 8 public int ReturnValue2()
 9 {
10 MyInt x = new MyInt();
11 x.MyValue = 3;
12 MyInt y = new MyInt();
13 y = x;
14 y.MyValue = 4;
15 return x.MyValue;
16 }
复制代码

执行结果为4。

我们来分析下原因,其实例1的跟以下代码所起效用一样:

复制代码
1 public int ReturnValue()
2 {
3 int x = 3;
4 int y = x;
5 y = 4;
6 return x;
7 }
复制代码

在栈上x和y分别占用一块内存区,互不干扰。

而例2,与以下代码所起效用一样:

复制代码
1 public int ReturnValue2()
2 {
3 MyInt x;
4 x.MyValue = 3;
5 MyInt y;
6 y = x;
7 y.MyValue = 4;
8 return x.MyValue;
9 }
复制代码

C#中堆和栈的区别分析

一、预备知识—程序的内存分配 一个由C/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其 操作方式类似于数据结构中的栈...
  • Andeewu
  • Andeewu
  • 2013年08月07日 19:06
  • 2017

【C#】堆、栈和堆栈的区别

导读:今天看视频,就看到了堆、栈这一块了。顿时就来劲儿了,为什么呢,...
  • u013034889
  • u013034889
  • 2014年10月24日 14:24
  • 2271

线程堆栈(Thread Stack)和托管堆(Managed Heap)

内存格局通常分为四个区 全局数据区:存放全局变量,静态数据,常量 代码区:存放所有的程序代码 栈区:存放为运行而分配的局部变量,参数,返回数据,返回地址等, 堆区:即自由存储区       ...
  • xxdddail
  • xxdddail
  • 2014年07月04日 17:23
  • 2044

堆和栈的概念和区别

在说堆和栈之前,我们先说一下JVM(虚拟机)内存的划分:       Java程序在运行时都要开辟空间,任何软件在运行时都要在内存中开辟空间,Java虚拟机运行时也是要开辟空间的。JVM运行时在内存...
  • pt666
  • pt666
  • 2017年04月27日 19:06
  • 1210

Java 堆和栈的区别

1、概述在Java中,内存分为两种,一种是栈内存,另一种就是堆内存。2、堆内存1.什么是堆内存? 堆内存是是Java内存中的一种,它的作用是用于存储Java中的对象和数组,当我们new一个对象或者创建...
  • u011546655
  • u011546655
  • 2016年08月10日 12:59
  • 5106

数据结构中的堆和栈 与 内存分配中的堆区和栈区 分析

比较全面的总结了诸多版本,知识无国界,感谢各位的辛勤劳作。 在计算机领域,堆栈是一个不容忽视的概念,我们编写的C/C++语言程序基本上都要用到。但对于很多的初学着来说,堆栈是一个很模糊的概念...
  • hustyangju
  • hustyangju
  • 2014年05月08日 10:59
  • 2484

Java中的基础----堆与栈的介绍、区别

堆和栈都是Java中常用的存储结构,都是内存中存放数据的地方。 栈:基本数据类型的变量(int、short、long、byte、float、double、boolean、char等)以及对象的引用变量...
  • Emira_J
  • Emira_J
  • 2016年04月24日 11:16
  • 15122

单片机全局变量 局部变量 堆与栈 的区别

单片机全局变量 局部变量 堆与栈 的区别 局部变量空间,就是堆栈空间,也就是栈空间。 从局部变量声明的时候,它就在堆栈空间了,而不是调用函数的时候,才让它入栈的。 定义一个局部变...
  • zsh2011
  • zsh2011
  • 2013年04月21日 20:07
  • 1448

编程_堆和栈的区别

本文简单介绍了堆和栈的区别。
  • cc214042
  • cc214042
  • 2016年10月03日 18:19
  • 1140

iOS中堆和栈的区别

objective-c 对象所占内存总是分配在“堆空间”,并且堆内存是由你释放的,即release。 栈是由编译器管理自动释放的,在方法中(函数体)定义的变量通常在栈内。 1.栈区(sta...
  • WallaceWang_
  • WallaceWang_
  • 2017年05月21日 10:53
  • 295
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C#中堆和栈的区别分析
举报原因:
原因补充:

(最多只允许输入30个字)