要说堆、栈的区别,首先要有一个概念:一个进程的4G虚拟地址空间划分:(如图) 整体上从低地址到高地址可以划分为:3G的用户空间和1G的内核空间。 用户空间中:从低地址到高地址分别为:128M的不可以访问区;.text指令段;.data数据段;.bss数据段;... ;heap堆区(自低地址向高地址开辟空间);....;stack栈区(自高地址向低地址开辟空间);...... ---------------------------------------------(简要说明各个段的作用 详细介绍下面两份) 堆区(heap)。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时有可能由OS回收。 栈区。由编译器自动分配释放,存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈。每当一个函数被调用,该函数返回地址和一些关于调用的信息,比如某些寄存器的内容,被存储到栈区。然后这个被调用的函数再为它的自动变量和临时变量在栈区上分配空间,这就是C实现函数递归调用的方法。每执行一次递归函数调用,一个新的栈框架就会被使用,这样这个新实例栈里的变量就不会和该函数的另一个实例栈里面的变量混淆。 下面一个例子: void f() { int* p=new int[5]; } 这条短短的一句话就包含了堆与栈,关键new指示分配了一块堆内存,而指针P分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小,然后调用operator new分配内存,然后返回这块内存的首地址,放入栈中。 二者区别: 1、申请方式不同:堆的话,程序员动态申请,new和malloc()申请的空间;栈,由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。 2、管理方式不同。堆,程序员自己管理,若没有free、delete释放,程序结束后由OS释放。栈,系统管理。 3、空间大小不同。栈是自高址向低地址扩展的结构,是一块连续内存区域,栈顶的地址和栈的最大容量是系统预先规定好的,当申请的空间超过栈的剩余空间时,将提示溢出。因此,用户能从栈获得的空间较小,通常为1M,也有2M的,可修改。 Linux下,可用命令:ulimit -s 查看栈大小 并重新设置大小。 堆是自低地址向高地址扩展的数据结构(它的生长方向与内存的生长方向相同),是不连续的内存区域。因为系统是用链表来存储空闲内存地址的,且链表的遍历方向是由低地址向高地址。由此可见,堆获得的空间较灵活,也较大。堆的大小受限于计算机系统中有效的虚拟内存。一般来讲在32位系统下,堆内存可以达到2.9G的大小。(除去1G的内核空间,几乎占满3G的用户空间) 4、申请后系统的响应: 栈:只要栈的空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 堆:操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的free语句才能正确的释放本内存空间。另外,找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 对于堆来讲,频繁的malloc/free势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈就不会存在这个问题。 堆上频繁的new delete会产生内存碎片,栈上是连续的空间则不会有这个问题。 6)分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函 数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法,在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是 由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。 推荐一篇文章,详细介绍4G虚拟地址空间中各个段。 |
|