栈与堆的区别
Thu Sep 12 2024
理解栈与堆
如何理解
栈和堆其实是我们的两种使用内存的2种不同方式,内存使用涉及两个基本问题:内存分配与内存回收 想要理解栈与堆的区别,就需要从内存分配和回收的角度去分析思路 总结下来就是
- 使用内存时,需要考虑内存分配和回收的问题
- 栈和堆就是使用内存的两种不同方式
- 堆的通用性好,但是需要配合复杂的内存管理使用
- 栈针对于函数做了优化,性能好,但通用性差
堆
堆的设计核心思路是:关注点分离
- 把内存的分配回收和应用的实际读写内存分开
- 内存的管理模块去控制内存的分配和回收,并为应用程序提供接口
- 应用程序通过接口去完成内存的分配和释放
但是在实际应用中,应用程序会要求分配不同大小块,可能是24字节,8字节等等 这个时候我们就可以把内存分配成不同大小的块,每种大小都对应这自己的freeList 然后选择比应用程序要求大,但又最接近的块返回,以减少内存的浪费
PS:如果是JAVA语言,则不需要自己调用free(),而是为堆引用自动回收机制的(GC)
栈
栈是针对与函数调用做的优化,它的设计核心思路: 记录 - 恢复
- 函数开始执行前,记录内存的使用情况
- 函数结束执行后,直接恢复到开始的记录的状态(这就相当于回收了当前函数分配的内存)注:栈的高效主要就来自于对栈使用方式的限制
但这样的优化思路会出现两个问题
- 函数本身占用的内存不是很大,即等不到函数结束时,内存就已经耗尽了 解决方方法:让堆分摊内存压力 涉及基本类型变量和引用(指针)可以放在栈上 消耗内存大的放在堆上
- 函数执行前的环境不能太复杂,如果环境太复杂那栈执行的记录和和恢复花销就太大了,优化就没有太大的意义 解决方方法:限制栈的操作,只能在栈顶处进行操作(push/pop) 通过这个限制就只需要记录“栈顶位置”就可以描述函数执行前的状态
通过此处两个问题就可以看出栈是非常受限制的数据结构,但是越是受限的越是简单,栈的需求就是要简单
在执行次代码会有三个阶段
阶段一
1.准备参数 2.调用avg
阶段二
1.执行avg() 2.准备返回值
进入avg函数内部,会先保存avg正式开始执行前的栈的状态,有两条指令
push ebp 此时ebp里面记录的是函数f()的栈帧起始地址 现在的操作是把ebp的栈帧起始地址暂存到栈上,从而腾出ebp push就是压栈
mov ebp,esp 把esp指向的栈顶地址,暂存到ebp里 PS:esp这个寄存器总是指向栈顶
阶段三
1.回收avg()占用的内存
mov esp ebp 这个指令是与mov ebp,esp相对的。
达成的效果是:移除avg()的栈帧,回收avg()占用的内存 即:把栈恢复到执行avg()之前的状态
阶段四
1.恢复函数f()的栈帧
- pop ebp 即:恢复函数f()的栈帧,与push ebp相对 pop就是出栈
阶段五
1.返回函数f(),继续执行代码
- ret ret指令会弹出栈顶元素作为指令的返回地址,CPU会从返回地址,继续执行代码
注:此处参数5和7还在栈上,函数f()可以根据自己的需要决定是否弹出这两个参数
主要是关注加粗命令,这四条指令就完成了函数栈空间的回收