一、定义
Stack(栈):函数调用时自动分配、自动释放的内存,由系统管。
Heap(堆):手动申请、手动释放(或GC回收)的内存,由你管。
二、内存布局
从上到下(高地址 → 低地址):
1. Kernel Space(内核空间,用户不能直接访问)
2. Stack(栈)
1) 从高地址往低地址生长
2) 函数调用、局部变量、返回地址、寄存器上下文
3. Memory Mapping Segment(mmap 区,动态库、文件映射)
4. Heap(堆)
1) 从低地址往高地址生长
2) malloc / new / free / delete 管理
5. Data Segment
★ 已初始化全局/静态变量
6. BSS Segment
★ 未初始化全局/静态变量(默认 0)
7. Text Segment
★ 代码段、可执行指令
三、 Stack 详细说明
定义
1) 栈是一块连续内存,遵循 LIFO(后进先出)。
2) 每调用一个函数,就压入一个 Stack Frame(栈帧)。
栈帧里一般存:
a) 函数参数
b) 局部变量
c) 返回地址
d) 旧栈帧指针(ebp/rbp)
e) 保存的寄存器
常用寄存器
ebp :栈帧基址指针(固定不动)
esp :栈顶指针(一直变)特点
特点
- 自动分配、自动释放
- 速度极快
- 空间小(默认几 MB)
- 不会碎片
- 线程独有(每个线程一个栈)
x86/x64函数调用栈对比
四、Heap 详细说明
定义
堆是一块大内存池,用于动态分配,由程序手动控制。
管理方式
- C: malloc / calloc / realloc / free
- C++: new / delete
- Java/Go/Python:GC 自动回收
特点
- 空间大(几 GB)
- 分配慢
- 会产生内存碎片
- 进程共享(多线程共享堆)
- 必须手动管理/GC,否则泄漏
五、 最核心对比表
6-1 栈的应用
int add(int a, int b) {
int x = 10;
int y = 20;
return x + y + a + b;
}
int main() {
add(100, 200);
return 0;
}
函数栈结构
函数汇编
; 进入 add 函数,建立栈帧
push ebp ; 保存旧 ebp
mov ebp, esp ; ebp = 栈顶
sub esp, 8 ; 给 2 个局部变量开空间(8 字节)
; 给局部变量赋值
mov dword ptr [ebp-4], 10 ; x = 10
mov dword ptr [ebp-8], 20 ; y = 20
; 取值计算
mov eax, [ebp-4] ; eax = x
add eax, [ebp-8] ; eax += y
add eax, [ebp+8] ; eax += a
add eax, [ebp+12] ; eax += b
; 销毁栈帧
mov esp, ebp
pop ebp
ret
6-2 堆的应用
// 全局变量 → Data / BSS 段
int global_var = 10;
void func() {
// 局部变量 → Stack
int stack_var = 20;
// 指针本身在 Stack
// 指向的内存在 Heap
int* heap_ptr = (int*)malloc(sizeof(int));
*heap_ptr = 30;
// 必须 free,否则泄漏
free(heap_ptr);
}
七、 应用中常见问题
1)栈溢出(Stack Overflow):递归太深、局部数组太大。
2)堆泄漏(Memory Leak):malloc 没 free。
3)野指针:free 后还在用。
4)线程安全:栈变量线程安全;堆变量需要锁。