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

One*_*ero 3 c malloc memory-management

比较以下两个:

int arr[10]; // stack memory
int* x = malloc(sizeof(int)*10); // heap memory
Run Code Online (Sandbox Code Playgroud)

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

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

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

Gen*_*ene 6

调用mallocfree通常需要几个100的和平均根据指令数1000对实施,堆的电流碎片和其他细节之间的某处。

为一个函数分配和取消分配堆栈帧需要大约4条指令。


Ant*_*ala 5

int a[10]堆栈基本上是免费的。在 x86(即 16、32 或 64 位模式)上的函数序言中,当前堆栈指针被复制到基指针;然后堆栈指针向下调整(通过SUB操作码),以便在基指针和堆栈指针之间有足够的空间来存储该函数帧中的所有局部变量。

由于局部变量未在 C 中初始化,因此定义

int a[10];
Run Code Online (Sandbox Code Playgroud)

开销可能不会少于

int a[432], b[234], c[234], d[123], e[123], f[34], g[23];
Run Code Online (Sandbox Code Playgroud)

例如,对于第一个,编译器可以编写一个序言,使堆栈指针减去 16 个字节,第二个则减去 1216 个字节。

然后在函数退出时,由于编译器已经将旧的堆栈指针存储到基指针寄存器中,基指针寄存器的内容再次存储到堆栈指针寄存器中,不需要其他清理。


现在,malloc事情变得更棘手了。如果程序是多线程的,则每个线程都需要有一个竞技场/池,或者必须有某种排除以锁定其他线程。malloc需要找到一个足够大的块,然后更新它的簿记。然后也free必须执行相同的步骤 - 锁定互斥设备并更新簿记。


总而言之,一个malloc/free对至少有几十条指令的开销,而堆栈变量原则上是自由的。


tex*_*uce 1

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

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

但如果你不这样做十亿次,它应该可以忽略不计

  • @texasbruce 这是不对的。堆内存由运行在用户空间的 C 库管理。在我研究过的十几个 C 库中,没有一个会调用内核调用数十亿次。 (2认同)