堆
堆内存是由 new 分配的内存块,由 delete 删除。堆由程序控制,编译器不负责管理堆,一般一个 new 对应一个 delete,如果程序没有手动释放内存,则在程序结束后操作系统会自动回收。
堆可以动态地扩展和收缩。
栈
栈内存由编译器进行管理,在需要时分配,在不需要的时候自动清理的变量的存储区。
栈内存储的变量通常为:
- 局部变量
- 函数参数
用户栈位于用户虚拟地址空间的顶部,编译器用它来实现函数的调用。
自由存储区
自由存储区和堆十分类似,它是由 malloc 和 free 来管理的内存块。
全局 / 静态存储区
全局变量和静态变量被分配到同一块内存中。
在以前的 C 语言中,全局变量由分为初始化的和未初始化的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量与静态变量在相邻的另一块区域。同时,未初始化的对象存储区可以通过 void* 来访问和操作,程序结束后由操作系统回收。
常量存储区
常量存储区比较特殊,里面存放的是常量,不允许修改。
堆与栈的区别
例如:
int* p = new int[5];
new 分配了一块堆内存,指针 p 在栈内存中,这条语句的意思是:在栈内存中存放了一个指向堆内存的指针 p。程序会先确定在堆中分配内存的大小,然后调用 new 分配内存,接着返回内存的首地址,放入栈中。
管理方式
栈由编译器自动管理,无需手动控制。
堆由程序员手动控制,容易产生内存泄漏。
空间大小
堆内存一般较大,几乎没有限制。栈内存默认空间较小,但可以在工程的设置中调整。
碎片问题
堆由于频繁的 new / delete ,会造成内存空间的不连续,从而造成大量碎片。
栈由于是先进后出的序列,所以不会有一块内存从栈的中间弹出,在内存块弹出之前,该内存块上面的内容都已被弹出。
增长方向
堆是向上增长的,即向着内存地址增大的方向增长。
栈是向下增长的,即向着内存地址减小的方向。
分配方式
堆都是动态分配的,没有静态分配的堆。
栈有两种分配方式:静态分配和动态分配。静态分配是编译器完成的,如局部变量的分配。动态分配有 malloc 函数进行分配。但栈的动态分配合堆不同,栈的动态分配是由编译器进行释放,无需手工实现。
分配效率
栈是由机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行。因此,栈的效率比较高。
堆则是 C/C++ 函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间,就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。