林炳文Evankaka原创作品。
转载请注明出处http://blog.csdn.net/evankaka
在CC++中,通常能够把内存理解为4个分区:栈、堆、全局/静态存储区和常量存储区。
以下我们分别简单地介绍一下各自的特点。
一. 区域划分
堆: 是大家共同拥有的空间,分全局堆和局部堆。
全局堆就是全部没有分配的空间。局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,执行过程中也能够向系统要额外的堆。可是记得用完了要还给操作系统,要不然就是内存泄漏。
栈:是个线程独有的,保存其执行状态和局部自己主动变量的。栈在线程開始的时候初始化,每一个线程的栈互相独立,因此,栈是 thread safe的。每一个C ++对象的数据成员也存在在栈中,每一个函数都有自己的栈,栈被用来在函数之间传递參数。
操作系统在切换线程的时候会自己主动的切换栈。就是切换 SS/ESP寄存器。
栈空间不须要在高级语言里面显式的分配和释放。
一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack): 由编译器自己主动分配释放 ,存放函数的參数值,局部变量的值等。其操作方式相似于数据结构中的栈。
2、堆区(heap) : 一般由程序猿分配释放, 若程序猿不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是相似于链表。
3、全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域。 未初始化的全局变量和未初始化的静态变量在相邻的还有一块区域。 程序结束后有系统释放 。
4、文字常量区:常量字符串就是放在这里的, 程序结束后由系统释放 。
5、程序代码区:存放函数体的二进制代码。
图示1:可执行程序在存储器中的存放
二.演示样例代码
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456放在常量区。编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
三.堆和栈
3.1申请方式
stack: 遵循LIFO后进先出的规则。它的生长方向是向下的,是向着内存地址减小的方向增长,栈是系统提供的功能,特点是高速高效。缺点是有限制,数据不灵活。
由系统自己主动分配。 比如,声明在函数中一个局部变量 int b; 系统自己主动在栈中为b开辟空间
heap: 对于堆来讲,生长方向是向上的,也就是向着内存地址添加的方向。
须要程序猿自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 =new char[10];
可是注意p1、p2本身是在栈中的。
3.2 申请后系统的响应
栈:仅仅要栈的剩余空间大于所申请空间,系统将为程序提供内存。否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空暇内存地址的链表。当系统收到程序的申请时。 会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空暇结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小。这样,代码中的delete语句才干正确的释放本内存空间。
另外,因为找到的堆结点的大小不一定正好等于申请的大小。系统会自己主动的将多余的那部分又一次放入空暇链表中。
3.3申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数)。假设申请的空间超过栈的剩余空间时,将提示overflow。
因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是因为系统是用链表来存储的空暇内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。
堆的大小受限于计算机系统中有效的虚拟内存。
由此可见,堆获得的空间比較灵活,也比較大。
3.4申请效率的比較:
栈:由系统自己主动分配。速度较快。但程序猿是无法控制的。
堆:是由new分配的内存,一般速度比較慢,并且easy产生内存碎片,只是用起来最方便.
另外。在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,尽管用起来最不方便。可是速度快,也最灵活。
3.5堆和栈中的存储内容
栈: 在函数调用时。第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址。然后是函数的各个參数。在大多数的C编译器中,參数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后。局部变量先出栈,然后是參数,最后栈顶指针指向最開始存的地址。也就是主函数中的下一条指令,程序由该点继续执行。
堆:通常是在堆的头部用一个字节存放堆的大小。堆中的详细内容有程序猿安排。
3.6存取效率的比較
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读取字符,显然慢了。
3.7小结:
堆和栈的差别能够用例如以下的比喻来看出:
使用栈就象我们去饭馆里吃饭,仅仅管点菜(发出申请)、付钱、和吃(使用)。吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作。他的优点是快捷。可是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴。比較麻烦。可是比較符合自己的口味,并且自由度大。
1、内存分配方面:
堆:一般由程序猿分配释放, 若程序猿不释放,程序结束时可能由OS回收 。
注意它与数据结构中的堆是两回事,分配方式是相似于链表。可能用到的keyword例如以下:new、malloc、delete、free等等。
栈:由编译器(Compiler)自己主动分配释放,存放函数的參数值,局部变量的值等。其操作方式相似于数据结构中的栈。
2、申请方式方面:
堆:须要程序猿自己申请。并指明大小。在c中malloc函数如p1 = (char *)malloc(10);在C++中用new运算符,可是注意p1、p2本身是在栈中的。
因为他们还是能够觉得是局部变量。
栈:由系统自己主动分配。 比如,声明在函数中一个局部变量 int b;系统自己主动在栈中为b开辟空间。
3、系统响应方面:
堆:操作系统有一个记录空暇内存地址的链表。当系统收到程序的申请时,会遍历该链表。寻找第一个空间大于所申请空间的堆结点,然后将该结点从空暇结点链表中删除,并将该结点的空间分配给程序。另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样代码中的delete语句才干正确的释放本内存空间。另外因为找到的堆结点的大小不一定正好等于申请的大小,系统会自己主动的将多余的那部分又一次放入空暇链表中。
栈:仅仅要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
4、限制大小方面:
堆:是向高地址扩展的数据结构。是不连续的内存区域。这是因为系统是用链表来存储的空暇内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比較灵活,也比較大。
栈:在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的。在WINDOWS下。栈的大小是固定的(是一个编译时就确定的常数),假设申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
5、效率方面:
堆:是由new分配的内存,一般速度比較慢,并且easy产生内存碎片,只是用起来最方便。另外。在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存。尽管用起来最不方便。可是速度快。也最灵活。
栈:由系统自己主动分配。速度较快。
但程序猿是无法控制的。
6、存放内容方面:
堆:通常是在堆的头部用一个字节存放堆的大小。堆中的详细内容有程序猿安排。
栈:在函数调用时第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址然后是函数的各个參数,在大多数的C编译器中。參数是由右往左入栈,然后是函数中的局部变量。 注意: 静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈。然后是參数,最后栈顶指针指向最開始存的地址。也就是主函数中的下一条指令。程序由该点继续执行。
7、存取效率方面:
堆:char *s1 = "Hellow Word";是在编译时就确定的;
栈:char s1[] = "Hellow Word"。 是在执行时赋值的。用数组比用指针速度要快一些,因为指针在底层汇编中须要用edx寄存器中转一下,而数组在栈上直接读取。
在C++中,内存分成5个区,他们各自是堆、栈、自由存储区、全局/静态存储区和常量存储区。
栈。就是那些由编译器在须要的时候分配。在不须要的时候自己主动清楚的变量的存储区。
里面的变量通常是局部变量、函数參数等。 堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要相应一个delete。
假设程序猿没有释放掉,那么在程序结束后。操作系统会自己主动回收。
自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,只是它是用free来结束自己的生命的。 全局/静态存储区,全局变量和静态变量被分配到同一块内存中。在曾经的C语言中。全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。 常量存储区,这是一块比較特殊的存储区,他们里面存放的是常量,不同意改动(当然。你要通过非正当手段也能够改动。并且方法非常多)
四 总结
依据上面的内容,分别将栈和堆、全局/静态存储区和常量存储区进行对照,结果例如以下。
表1 栈和堆的对照
栈 | 堆 | |
存储内容 | 局部变量 | 变量 |
作用域 | 函数作用域、语句块作用域 | 函数作用域、语句块作用域 |
编译期间大小是否确定 | 是 | 否 |
大小 | 1MB | 4GB |
内存分配方式 | 地址由高向低降低 | 地址由低向高添加 |
内容能否够改动 | 是 | 是 |
全局/静态存储区 | 常量存储区 | |
存储内容 | 全局变量、静态变量 | 常量 |
编译期间大小是否确定 | 是 | 是 |
内容能否够改动 | 是 | 否 |
五、使用范例
1、一条进程在内存中的映射
假设如今有一个程序,它的函数调用顺序例如以下:
main(...) ->; func_1(...) ->; func_2(...) ->; func_3(...)。即:主函数main调用函数func_1; 函数func_1调用函数func_2; 函数func_2调用函数func_3。
当一个程序被操作系统调入内存执行, 其相应的进程在内存中的映射例如以下图所看到的:
注意:
l 随着函数调用层数的添加。函数栈帧是一块块地向内存低地址方向延伸的;
l 随着进程中函数调用层数的降低(即各函数调用的返回)。栈帧会一块块地被遗弃而向内存的高址方向回缩;
l 各函数的栈帧大小随着函数的性质的不同而不等, 由函数的局部变量的数目决定。
l 未初始化数据区(BSS):用于存放程序的静态变量,这部分内存都是被初始化为零的;而初始化数据区用于存放可执行文件中的初始化数据。这两个区统称为数据区。
l Text(代码区):是个仅仅读区,存放了程序的代码。不论什么尝试对该区的写操作会导致段违法出错。
代码区是被多个执行该可执行文件的进程所共享的。
l 进程对内存的动态申请是发生在Heap(堆)里的。随着系统动态分配给进程的内存数量的添加,Heap(堆)有可能向高址或低址延伸, 这依赖于不同CPU的实现。但一般来说是向内存的高地址方向增长的。
l 在未初始化数据区(BSS)或者Stack(栈区)的增长耗尽了系统分配给进程的自由内存的情况下,进程将会被堵塞, 又一次被操作系统用更大的内存模块来调度执行。
l 函数的栈帧:包括了函数的參数(至于被调用函数的參数是放在调用函数的栈帧还是被调用函数栈帧, 则依赖于不同系统的实现)。函数的栈帧中的局部变量以及恢复该函数的主调函数的栈帧(即前一个栈帧)所须要的数据,包括了主调函数的下一条执行指令的地址。
2、 函数的栈帧
函数调用时所建立的栈帧包括以下的信息:
1) 函数的返回地址。返回地址是存放在主调函数的栈帧还是被调用函数的栈帧里。取决于不同系统的实现;
2) 主调函数的栈帧信息, 即栈顶和栈底;
3) 为函数的局部变量分配的栈空间;
4) 为被调用函数的參数分配的空间取决于不同系统的实现。
注意:
l BSS区(未初始化数据段):并不给该段的数据分配空间。仅仅是记录了数据所需空间的大小。
l DATA(初始化的数据段):为数据分配空间,数据保存在目标文件中。
林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka