比较以下两个:
int arr[10]; // stack memory
int* x = malloc(sizeof(int)*10); // heap memory
两者基本上都是分配10个整数变量。但是,我们经常说第一个便宜得多(分配和释放更快)b / c它只是向前移动堆栈指针。
我们知道所有程序都在虚拟内存空间中运行,并且只有程序实际使用的部分将被分配(也称为映射到物理内存),未使用的部分将保持虚拟。这就是操作系统如何在不同程序之间共享物理内存。
所以这是我的问题。在我看来,无论你如何分配内存(在堆栈上或堆上),一个共同点是操作系统需要找到&保留物理内存块,并将虚拟内存地址(无论是堆栈还是堆栈)映射到物理地址。对于解除分配,当系统需要删除映射&释放物理内存。那么为什么堆栈分配/解除分配更快?内存分配的最大开销在两者之间似乎相当公平。
答案 0 :(得分:5)
对malloc
和free
的调用通常需要介于几个100和几千个指令之间,具体取决于实现,堆的当前碎片和其他细节。
为函数分配和取消分配堆栈帧需要大约4条指令。
答案 1 :(得分:4)
堆栈上的SUB
基本上是免费的。在函数序言中的x86(是16,32或64位模式)上,当前堆栈指针被复制到基指针;然后向下调整堆栈指针(通过int a[10];
道操作码)向下调整,以便在基本指针和堆栈指针之间有足够的空间来存储 all 该函数帧中的局部变量。< / p>
由于局部变量未在C中初始化,因此定义
int a[432], b[234], c[234], d[123], e[123], f[34], g[23];
可能没有比
更少的开销malloc
e.g。对于第一个,编译器可以编写一个序言,导致堆栈指针减去16个字节,而第二个减去1216个字节。
然后在函数退出时,由于编译器已将旧堆栈指针存储到基址指针寄存器,基址指针寄存器内容再次存储到堆栈指针寄存器,并且不需要进行其他清理。
现在,对于malloc
来说,事情变得棘手了。如果程序是多线程的,则每个线程都需要一个竞技场/池,或者必须有某种排除来锁定其他线程。 free
需要找到足够大的块,然后更新其簿记。然后malloc
必须执行相同的步骤 - 锁定互斥设备并更新簿记。
总而言之,单个free
/ name
对至少有数十条开销指令,而堆栈变量原则上是免费的。
答案 2 :(得分:2)
当程序进入函数时,会创建一个新的堆栈帧。在堆栈上分配变量时,会将其推入当前堆栈帧。退出函数时,将释放堆栈帧。这种创建和销毁数据的静态知识使得cpu可以轻松优化堆栈。
另一方面,堆更具动态性。它是开发人员使用malloc和free管理的一大块内存(而不是许多小的线性帧)。没有真正的方法来优化这样的内存。
使用指针而不是直接引用堆分配这一事实使这种差异更加突出。每次访问堆都需要事先取消引用操作。堆栈上的每个访问都是本地内存,并且可以在cpu中轻松缓存。
上述所有内容的细节太多,太复杂,无法在此处介绍,但这应该为理解提供基本概述。
答案 3 :(得分:1)
因为程序的堆栈空间是预先分配的,不涉及系统调用。
堆空间分配需要系统调用,这涉及到内核的一系列操作,因此速度较慢。
但是,如果你不做十亿次,那就应该可以忽略