内存从逻辑上划分两大块:栈

时间:2021-09-04 08:11:00

线程仓库:简称栈 Stack

托管堆: 简称堆 Heap

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

代码段1:

public int AddFive(int pValue) { int result; result = pValue + 5; return result; }


代码段2:

public 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)(简称堆)有个清楚认识,本立而道生。如果你想提高措施性能,理解栈和堆,必需的!

本文就从栈和堆,类型变量展开,对我们写的措施进行厨子解牛。

C#措施在CLR上运行的时候,内存从逻辑上划分两大块:栈,堆。这俩根基元素构成我们C#措施的运行环境。


一,栈 vs 堆:区别?

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

如图1,栈与堆示意图

内存从逻辑上划分两大块:栈

(图1)

栈内存无需我们打点,也不受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),它所指向的另一块内存区才是我们真正的数据或者类型。如图2:

内存从逻辑上划分两大块:栈



(图2)

指令:

后文对指令再做介绍。


三,如何分配?


我们先看一下两个不雅概念:

不雅概念1,引用类型总是被分配在堆上。(正确?)

不雅概念2,值类型和指针总是分配在被界说的处所,他们不必然被分配到栈上。(这个理解起来有点难度,需要慢慢来)

上文提及的栈(Stack),在措施运行的时候,每个线程(Thread)城市维护一个本身的专属线程仓库。

当一个要领被挪用的时候,主线程开始在所属措施集的元数据中,查找被挪用要领,然后通过JIT即时编译并把功效(一般是本地CPU指令)放在栈顶。CPU通过总线从栈顶取指令,驱动措施以执行下去。

下面我们以实例来详谈。

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

public int AddFive(int pValue) { int result; result = pValue + 5; return result; }

当AddFive要领开始执行的时候,要领参数(parameters)则在栈上分配。如图3:

内存从逻辑上划分两大块:栈



(图3)

注意:要领并不在栈中存活,图示仅供参考。

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


(图4)

当要领内部开始执行的时候,变量result被分配在栈上,如图5:

内存从逻辑上划分两大块:栈



(图5)