栈(Stack) 是 “CPU 自动管理的临时工作台”,堆(Heap) 是 “程序员申请的大仓库”;
前者 随方法进出自动生死,后者 手动 or GC 清理,速度差一个数量级。
栈(Stack) 是 “CPU 自动管理的临时工作台”,堆(Heap) 是 “程序员申请的大仓库”;
前者 随方法进出自动生死,后者 手动 or GC 清理,速度差一个数量级。
前者 随方法进出自动生死,后者 手动 or GC 清理,速度差一个数量级。
一、生命周期:谁掌控生死?
表格
场景 | 栈 | 堆 |
---|---|---|
分配时机 | 方法调用 瞬间 CPU 压栈 | new / malloc 时向操作系统申请 |
释放时机 | 方法 return 即弹出 | 手动 free / 垃圾回收 非确定 |
是否确定性 | ✅ 编译期就定好 | ❌ 运行期动态决定 |
典型例子int x = 3;
→ 栈;new Person()
→ 堆。
二、速度差异:CPU 缓存 vs 系统调用
表格
指标 | 栈 | 堆 |
---|---|---|
分配/释放速度 | 1 条指令(移动 esp/rsp) | 用户态→内核态→堆管理器,百条指令+锁 |
局部性 | 连续压栈,CPU 缓存友好 | 可能碎片化,缓存命中率低 |
并发成本 | 无锁 | 需 堆锁 or 线程局部分配 |
数量级:栈分配 纳秒级,堆分配 微秒级。
三、空间与限制
表格
维度 | 栈 | 堆 |
---|---|---|
默认大小 | Windows 1 MB / Linux 8 MB 线程栈 | 仅受 进程地址空间 限制(2-128 GB) |
大块数据 | 不宜(容易 StackOverflow) | 适合 大对象、数组、字符串 |
碎片问题 | 无(先进后出) | 有 → 需 压缩/整理 |
四、值类型 vs 引用类型 映射
csharp
void Foo()
{int x = 10; // 栈Point p = new Point(); // 栈(值类型)String s = new String(); // 引用本身在栈,**对象在堆**
}
五、一张图速记
方法调用
┌---------┐
│ 参数 │ ← 栈顶
│ 返回地址│
│ 局部变量│
└---------┘ ← esp 自动上下移动 = 1 条指令↓ new
┌-------------------------------┐
│ 托管堆 │
│ Person String byte[] ... │ GC 回收、可能碎片
└-------------------------------┘
六、金句
“栈由 CPU 自动压弹,方法结束即回收,速度快、空间小;
堆由 程序员或 GC 管理,生命周期灵活,空间大、速度慢、易碎片;
值类型和引用类型的 性能差异根源 就在于 栈 vs 堆 的分配策略。