内存中的‘堆’和‘栈’
我们主要讲内存上的堆区和栈区,数据结构中的堆和栈不作分析
理解:
- 栈区:栈上分配的内存空间是连续的,主要用来存放函数内部的局部变量以及函数参数等,栈由操作系统自动分配,自动释放,生命周期很短,函数调用完,栈就会立马被释放,且栈中不宜存放大量数据,可能会出现栈溢出的问题。
- 堆区:一般由程序员手动分配空间手动释放,在C语言中用malloc,realloc,calloc函数来开辟动态内存空间,在Java中用new关键字来开辟,C语言中用free()手动释放空间后,要记得对指向该内存空间的指针置空,不然会出现野指针(上一篇博客有详解)的问题,若程序员不释放,事后可能会由操作系统回收(最好还是自己手动释放,以免引起不必要的问题),Java中有垃圾回收机制。
- 全局区(.data和.bss):存放全局变量以及static修饰的静态变量,分为已初始化数据和未初始化数据,程序结束时就会被释放,全局变量和局部静态变量数据经常放在数据端(.data),未初始化的全局变量和静态变量一般放在叫(.bss)段里
- 只读常量区:常量字符串就是放在这里的,程序结束时被释放
- 代码区(.text):存放程序源代码编译后的机器指令
图解:
区别:
空间大小的不同:
- 堆:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。
- 栈:一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M。当然,这个值可以修改。
系统响应的区别:
- 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
- 堆:堆区的空间是不连续的,这是因为操作系统是用链表的形式存储空闲内存地址的,而且系统内有一个记录空闲内存地址的链表,所以在我们使用malloc等函数开辟内存空间时,操作系统会遍历该链表,找到第一个空间大于申请空间的堆节点(相当于一块空闲内存空间),将其从该链表中删除并分配给发送申请的程序,并且堆区的空间是要远大于栈区的。
存储内容的区别:
- 栈:存储函数调用时的参数,局部变量等。执行程序时,首先调用main函数,入栈的是主函数后的下一条将要执行的语句,然后是函数参数,一般来说参数是从右往左入栈的,然后是函数体内定义的局部变量,要注意函数内static修饰的变量是静态局部变量,是不会入栈的,函数调用完后,局部变量出栈,然后是参数,最后是函数后下一条可执行语句的地址,程序也直接跳转至该处,符合数据结构中栈的“先进后出”的规则,一次完整的函数调用就是栈帧。
- 堆:堆中具体存放的内容由程序员自己决定,我们需要使用较大内存空间时,往往会用到栈,对于大多数系统,会在这块的内存空间中的首地址处记录本次分配的大小,这样代码中的delete或free语句就能够正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会将多余的那部分重新放入空闲链表中。
执行效率的不同:
- 栈:函数调用时,系统自动为其分配栈空间,但是不被程序员自由控制,可操作性不强,函数调用完后,栈空间自动释放,
生命周期短,总结来说就一点,速度快! - 堆:堆上开辟空间的大小由程序员自己决定,而且使用起来比较方便,但是!使用完后一定要记得手动释放空间!释放玩空间后一定要记得将指向该空间的指针置空!
内存碎片问题:
- 堆:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。
- 栈:对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构。
- 所以通常情况下我们主张使用栈,而不是堆,但有时候需要大量的分配空间, 还是用堆好一些,具体情况具体对待
存储效率的不同(摘录自某大佬,了解了解就好):
char s1[] = “aaaaaaaaaaaaaaa”;
char *s2 = “bbbbbbbbbbbbbbbbb”;
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
#include
void main()
{
char a = 1;
char c[] = “1234567890”;
char *p =”1234567890”;
a = c[1];
a = p[1];
return;
}
对应的汇编代码
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到
edx中,再根据edx读取字符,显然慢了。
小结
- 栈空间由操作系统分配,速度快,生命周期短;堆空间由程序员手动开辟,速度慢,使用方便
- 栈空间远小于堆空间
- 栈内存放的是局部变量,函数参数,函数方法;堆内存放的内容由程序员自己决定
- 栈区是连续的地址空间。而堆区是不连续的
- 通常情况下推荐使用栈,需要大量分配空间时,推荐使用堆