为什么堆栈内存比C中的堆内存“便宜”?

时间:2016-04-09 03:46:04

标签: c memory-management malloc

比较以下两个:

int arr[10]; // stack memory
int* x = malloc(sizeof(int)*10); // heap memory

两者基本上都是分配10个整数变量。但是,我们经常说第一个便宜得多(分配和释放更快)b / c它只是向前移动堆栈指针。

我们知道所有程序都在虚拟内存空间中运行,并且只有程序实际使用的部分将被分配(也称为映射到物理内存),未使用的部分将保持虚拟。这就是操作系统如何在不同程序之间共享物理内存。

所以这是我的问题。在我看来,无论你如何分配内存(在堆栈上或堆上),一个共同点是操作系统需要找到&保留物理内存块,并将虚拟内存地址(无论是堆栈还是堆栈)映射到物理地址。对于解除分配,当系统需要删除映射&释放物理内存。那么为什么堆栈分配/解除分配更快?内存分配的最大开销在两者之间似乎相当公平。

4 个答案:

答案 0 :(得分:5)

mallocfree的调用通常需要介于几个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)

因为程序的堆栈空间是预先分配的,不涉及系统调用。

堆空间分配需要系统调用,这涉及到内核的一系列操作,因此速度较慢。

但是,如果你不做十亿次,那就应该可以忽略

相关问题