程序运行时数据需要存入内存,数据占用空间、存储位置由数据类型决定;运行程序会使用两块内存区域存放数据:栈 (Stack)、堆 (Heap)。
栈(Stack)
栈是一段内存数组,遵循后进先出 (LIFO) 数据结构,存储三类数据:
- 部分类型变量的值
- 程序当前执行上下文
- 传入方法的参数

栈的内存操作由系统自动管理,开发者无需手动操作;理解栈的底层逻辑,能更好读懂程序运行逻辑与 C# 官方文档。
栈的核心特性
- 仅能在栈顶新增、删除数据
- 向栈顶存入数据:入栈(Push)
- 从栈顶取出数据:出栈(Pop)
栈内存特点
- 内存分配释放速度极快,连续内存空间
- 生命周期绑定方法调用:方法执行完毕,栈帧自动销毁
- 存储:值类型变量、方法参数、局部变量、执行上下文

为了更好理解堆,我们看一个简单的例子
class Program
{
static void Main(string[] args)
{
int a = 10;
int b = 20;
int sum = Add(a, b);
}
static int Add(int x, int y)
{
return x + y;
}
}
Code language: C# (cs)
这个例子,很简单,从程序入口Main函数第一行代码开始,创建了一个变量a int类型的,第二行创建了创建了b变量,然后第三行调用Add函数,传入a和b的值,然后进入函数里面。x 的值是a的值,y的值是b的值,然后函数里面返回x+y的结果。然后把结果返回调用的地方 ,结果是30,Add函数执行结束,x,y会出栈,然后把结果复制给sum。sum的值就变成了30,然后main函数结束,程序退出。整个过程是这样,下面我们逐步分析。它们是如何在栈里面完整这个过程。
程序执行流程和入栈出栈的过程





Add函数执行完毕后,x和y属于Add内部的局部变量(函数参数),需要出栈,销毁变量x y先后出栈,腾空栈中的可用空间,保证系统流畅。
出栈后,x y 就没了。Add函数返回的结果,30,就到达调用的地方,准备下一步操作,也就是Int sum = 30;类似这样的操作。




读者记住一点,函数执行的时候,变量的创建是从上往下,所以创建的时候,先a -> b-> ,然后进入到Add函数,然后就 x ->y 然后Add结束,x y销毁,出栈,然后到sum入栈,之后Main结束,sum销毁,b销毁 a销毁,顺序是倒过来的。
堆(The Heap)
堆是一块内存区域,用于分配内存块存放特定数据对象;和栈不同,堆的数据可任意顺序存入、删除。
程序无法手动删除堆内对象,由 CLR(公共语言运行时)的垃圾回收器 GC自动清理无引用、无法访问的孤立堆对象;这省去了其他语言手动释放内存的易错操作。
堆就好比一张书桌,很大一块,你可用在上面随意放东西。而我们前面的栈,它就好比一个硬币筒,只能从一端进出。

GC 回收流程
- 程序在堆中创建 3 个对象,持有引用
- 程序不再使用其中一个对象,失去引用
- GC 检测到无引用孤立对象,释放其占用内存
- 回收完成,释放的内存可分配给新对象使用
堆内存特点
- 内存不连续,分配速度慢于栈
- 生命周期不受方法限制,由 GC 统一管理
- 存储:引用类型实例(class、数组、委托等),栈仅保存对象引用地址
为了更好演示,看一个例子,我们前面说的局部基本类型的变量是存放在栈中,如果是用户自定义的类型,通常是存放在堆的。下面看例子:
class Program
{
static void Main(string[] args)
{
int a = 10;
int b = 20;
Student tom = new Student("Tom", 12);
Student lucy = new Student("Lucy", 12);
}
}
class Student
{
//成员变量
private string name;
private int age;
//构造函数,后面会讲解
public Student(string name, int age)
{
this.name = name;
this.age = age;
}
//成员函数
public void DisplayInfo()
{
Console.WriteLine($"Name: {name}, Age: {age}");
}
}
Code language: C# (cs)
下面是Main函数中,四个变量a,b,tom,lucy 在内存中的存储示意图。和前面的栈的学习一样,从上往下,执行,a 入栈,然后b入栈,a和b的值 是直接存储在栈里面的。


例如上面 tom的数据是存储在堆中的0001区域,而栈中会存储00001的地址。存储地址是方便后面通过地址找到数据。
当Main函数执行完毕后,栈中的lucy:01010出栈,销毁,但是销毁的是01010这个地址,并不是真正在堆中的数据。这个时候,堆中的数据就变成了没有栈中的变量记录它的地址。也就是变成了这样。


内存区域
堆栈是内存的区域,是属于软件层面的。不是物理硬件层面的。
栈、堆都属于软件层面的内存逻辑划分,不是硬件物理分区,硬件本身没有专门标记 “栈区 / 堆区” 的硬件模块。
物理内存条 RAM,只有统一的内存芯片,只有连续的物理地址,没有栈、堆的区分标识
操作系统给每个运行程序分配一块独立虚拟内存空间,把这块虚拟内存预先划分成 4 大逻辑段:
代码段:存放程序编译后的指令
全局静态段:静态变量、常量
栈 (Stack) 段:程序主线程、子线程各自独立拥有一块栈内存
堆 (Heap) 段:一块大的共享内存池,动态分配
这里的栈、堆,只是操作系统给进程划分的逻辑区域,硬件无隔离。