先说一下什么是栈?

数据结构中的栈是一种先进后出的数据结构。

在C#的内存中有用到栈结构,一个线程拥有一个大概1mb的线程栈。一般拿来存放引用指针、值类型、方法的局部变量和实参等。

内存管理

栈是一种内存自我管理的结构,压栈自动分配内存,出栈自动清空所占内存。

但因此栈只能在一端对数据进行操作,也就是栈顶端进行操作。

方法调用与线程栈

方法的调用追踪就是在栈上完成的。比如我们有一个main方法(程序入口), 在main方法中会调用一个GetPoint的方法。在线程执行时,会将main方法压入栈底(包括编译好的方法指令,参数,和方法内部变量),然后再将GetPoint的方法压入栈底,GetPoint中没有调用其它方法,压栈完毕。出栈顺序是先进后出,也就是后进先出,栈顶的方法GetPoint先执行完毕,然后出栈,所占内存清空,接着main方法执行后出栈,所占内存清空。

再说一下什么是堆?

数据结构中的堆,是用数组实现的二叉树,所以它没有使用父指针或者子指针。堆根据“堆属性”来排序,“堆属性”决定了树中节点的位置。

CLR的堆是和栈对立的内存概念,而不是二叉树。一个程序用一个托管堆。托管堆一般拿来存放实例对象。

内存管理

相比栈只能在一端操作,堆中的数据可以随意存取。

因此堆的内存回收不是自我管理的,所以.NET推出了GC来管理堆内存。这里不展开GC。

说一下堆栈的协作?

1.当创建一个值类型时,CLR直接为其分配一个空间,这个空间分配在变量创建的地方

  • 如果值类型是在方法内部创建,则跟随方法入栈,分配到栈上存储。
  • 如果值类型是引用类型的成员变量,则跟随引用类型,存储在堆上。

2.当创建一个引用类型时,CLR会在堆上分配对象,在栈上分配指针

方法运行流程与堆栈?

执行一个方法比如

1
2
3
4
5
6
7
8
9
 void Start(){
int x = 1;
Test(x);
}

void Test(int y){
Student student = new Student();
student.id = y;
}

1.进来这个方法的时候会将 方法的返回地址、方法体内的局部变量 都压入栈。

2.在方法内再遇到方法的时候,同样会做1一样的事:将此方法的返回地址、方法体内的局部变量压入栈。

3.执行完内部方法(return)后,CPU指令指针返回到记录的返回地址上,继续执行外部代码。

4.如果方法内遇到new对象的情况,会在堆中申请对象,然后栈上会有一个指向对象的指针。

栈帧

简单点说就是一个栈的暂时状态,它反映着进入方法前的栈内状态。进入某方法前会执行上面说的一系列操作,退出这个方法后,会展开栈帧,回到进来前的运行状态 和 代码执行位置(CPU指向)。

序幕代码

也就是步骤1、2做的事,将方法内部的局部变量、参数、和返回地址压入栈。