栈和堆作为C++内存中的动态数据区,在程序执行过程中被动态的申请与释放,是C++内存管理中非常重要的环节。
在程序执行过程中,堆和栈有不同的分工,两者的区别为:
栈 | 堆 |
编译器自动申请与释放 | 用户手动申请与释放 |
连续存储结构,不会造成内存碎片 | 链式存储结构,容易造成内存碎片 |
编辑器自动处理,不会造成内存泄漏 | 容易造成内存泄漏 |
由高地址位向低地址位增长 | 由低地址位向高地址位增长 |
连续存储,内存开辟效率高 | 需要搜索足够大内存段,开辟效率低 |
栈空间小 | 堆空间大 |
内存先开辟、后释放 | 内存释放不受开辟顺序影响 |
在接触到栈与堆后,大部分初学者会有一个疑问:既然栈的开辟效率高,为什么不把栈区的最大空间设置大一些,如此不能能够提升程序的运行效率?
- 释放次序固定限制:释放的次序是固定的,必须是分配次序的反序。栈的分配和释放非常迅速(一个函数内栈的分配和回收 各自只需要一条指令),但是释放非常不灵活,极易造成浪费。对于复杂的程序对某对象分配的次序为abcdef,当abcde已经执行完时,不能被立即释放,必须等到f执行完释放后才能去释放edcba,导致此时间段edcba空间被占用。
- 操作系统内存限制:在程序执行时需要为每个线程开辟一段栈空间,操作系统可能同时包含上千个线程(2M*1000≈2G),如果堆开辟空间过大,将造成大量的内存浪费。
- 实际需求限制:栈区用来放临时变量,临时变量所需空间一般比较少,不需要过大内存。
此外,在栈出来之前,所有的内存都是堆,所有的内存都是供程序员自由的分配和释放。然而子函数调用时临时变量的申请与释放频繁,导致内存的碎片化,增大了堆的管理的复杂性。以此设计了栈区,弥补堆区内存管理的不足。开发者设计栈内存块并标记他们的长度,分配时选择或者分割一个合适的块,回收时合理的进行合并。因此,栈的出现是为了把开发者从频繁的申请与释放临时变量的工作中解放出来,目的是弥补堆的不足,让开发者高效的开发出极致大代码,当所需内存空间较大时,可以直接在堆区操作。