堆和栈的区别
堆是可用于动态分配的内存区域,由程序员手动管理(或是垃圾回收机制管理)。
栈用于管理函数调用,局部变量等的高效内存区域,由操作系统自动管理。
32位系统中,进程的虚拟内存空间分布如下,栈从高地址向低地址延伸,堆从低地址向高地址分配。
栈在操作系统的底层实现:
1、线程创建的时候,操作系统将为其分配1MB的栈空间(Windows系统默认)。
2、栈的使用:只需要移动栈指针。
3、x86平台下rsp为栈顶指针,rbp为栈底指针。(注意:栈从高地址向低地址延伸)。
4、进程栈(主线程栈)在进程启动时创建。
5、操作系统管理:
(1)函数调用时先将参数压栈(先放寄存器rcx,rdx,r8,r9,call进去后参数压栈),
(2)执行call指令,将返回地址(上一级函数的下一条指令地址)压栈,
(3)创建栈帧,保存上一级函数的栈底指针,设置rbp为新函数的栈底,
(4)为局部变量分配空间(注意数据对齐,数据所在的内存地址是数据长度的整数倍),
(5)最后还原栈底指针,栈顶指针,ret指令弹出返回地址。
64位系统一个int占4字节,后续写文章全部总结一遍。
堆在操作系统的底层实现
1、小块内存分配(小于阈值,例如128KB)
- malloc维护多个arena,每个arena包含一个或多个内存块。
- 分配请求首先尝试从现有的arena中分配内存。
- 如果arena中没有足够的可用内存,malloc可能会扩展现有的arena(可能需要调用 brk或sbrk),或者创建一个新的 arena。
- 释放的内存通常不会立即返还给操作系统,而是保留在arena中,以便后续的分配请求可以重用这些内存。
2、大块内存分配(大于或等于阈值)
- malloc使用mmap系统调用分配匿名内存。
- 释放的内存通过munmap系统调用返还给操作系统。
- 注意:mmap通常用于将文件内容映射到内存中,但它也可以用于分配匿名内存(即不与任何文件关联的内存)。
问题:
Q:栈为什么适合函数调用?
A:先进后出,具有严格的顺序,函数嵌套调用不会混乱,1234调用,4321释放。
Q:当函数递归调用过深时会发生什么?
A:栈溢出,触发SIGSEGV信号,程序崩溃。
Q:为什么栈的分配速度比堆快?
A:栈只需要移动指针,堆需要查找空闲块,处理碎片和系统调用。
Q:多线程程序当中,堆和栈如何实现隔离?
A:栈是线程私有,堆是进程共享,需要同步机制加锁。