这两天在研究MFC程序的内部实现原理,看了孙鑫老师的《VC深入详解》及姚领田老师的《精通MFC程序设计》。在分析MFC内部实现原理的章节,这两部书籍都首先提到全局对象theApp。要想深入理解全局对象的原理就不得不对程序的内存使用有所了解。下面就讲讲程序的内存区使用。
大的方面分,程序的内存区分为两部分:一类是静态存储区,另一类是动态存储区。
静态数据存储区又分为三类:只读数据区(RO Data)、已初始化的读写数据区(RW Data)、未初始化的读写数据区(BSS)。这三类存储区都是在程序的编译--连接阶段确定的。静态存储区在程序的运行过程中是不会发生变化的,其大小和位置在程序的运行过程中都是固定的,只有当程序退出时,静态存储区的内存才会被系统回收。例子如下:
static int a = 0; // 全局变量,已经初始化的读写数据区。内存中的值可以变,内存的大小和位置固定。
char *p1; // 全局变量,未初始化的读写数据区。内存中的值可以变,内存的大小和位置固定。
const int A = 10; // 全局变量,只读数据区。内存中的值不可以改变,内存的大小和位置固定。
void main(void)
{
}
动态存储区主要有两类:一类是栈(stack)内存区域,另一类是堆(heap)内存区域。他们都是在程序的运行过程中动态分配的。其大小在程序运行的过程中将动态的变化。从内存管理实现的角度看,栈内存使用线性的存储方式,堆内存使用链表的方式。栈内存是由编译器管理的,而堆内存是由程序调用具体的系统函数管理的。
栈内存的使用在很大程度上依赖于处理器的硬件机制。在处理器中,一般有一个寄存器来表示当前栈指针的位置。通常在内存中分配一块区域,这块区域的上界和下界之间是可用的栈内存区域。栈内存是一个先进后出的内存区域,就像叠在一起的盘子。栈内存的分配和释放都是由系统来完成的。
堆内存的分配和释放是由程序中的系统函数实现的。在堆内存的分配过程中,每次分配都将返回一个当前分配地址的指针。在释放的时候只需要对每个指针操作,那个指针所指向的内存区域就会被释放。c语言中用malloc、calloc等函数在堆上分配内存空间,用free释放堆内存空间;c++中用new分配堆内存空间,用delete释放堆内存空间。如果分配的堆内存空间没有被对应的函数释放,结果就造成了内存泄露。
在函数内部使用的变量、函数的参数以及返回值将使用栈空间。由于栈空间由编译器自动分配和释放,一般效率比较高。堆内存是由库函数来操作的,堆内存的需求可动态调整所以比较灵活。
void main(void)
{
int iValue; // 在栈上
int *p = &iValue; // 在栈上
double *dp = NULL; // 首先在栈上分配空间
dp = new double(10); // 在堆上分配空间,使栈上的指针指向这块堆内存
delete dp; // 释放堆内存
dp = NULL; // 防止出现野指针
}
一般来讲在 32 位系统下,堆内存理论上可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小好像是1M。当然,我们可以修改:打开工程,依次操作菜单如下:Project->Setting->Link,在 Category 中选中 Output,然后在 Reserve 中设定堆栈的最大值和 commit。注意:reserve 最小值为 4Byte;commit 是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。