5种程序内存区:全局/静态数据区、常量数据区、代码区、栈、堆 全局/静态数据区存储全局变量、全局静态变量、局部静态变量。类的静态成员变量也存储在这个区域,且只有一份拷贝,所有对象共享。可以自己编程写个有静态成员变量的类,然后看看不同对象中这个变量的内存地址,是一样的。 常量数据区存储常量字符串,字符串常量存储的区域不可修改。printf()函数的格式串也是存储于这个区域。常量存储区是4字节对齐的。 全局/静态数据区和常量数据区是在程序编译阶段已分配好,整个程序运行过程中始终存在的。 代码区存储程序代码 栈存储自动变量或局部变量,函数参数等 堆是由用户程序控制的存储区,存储动态产生的数据,通过new或malloc获得的内存是堆得内存,堆上分配内存时按16个字节对齐 上面所述的几个区域,对于C++的对象模型同样适用。即,一个对象的存储位置和它声明的类型有关,声明为全局变量,则存储在全局/静态数据区,通过new来创建,则存储在堆区域。 内存对齐 内存对齐,即分配时按对齐字节数来分配,譬如用new申请5个int,占20个字节,但由于堆是按16个字节对齐的,所以这5个int实际占32个字节的内存。这种方式虽然浪费一些内存,但是CPU在对齐方式下运行比较快,所以有益于程序性能。另外,stuct、uninon和class在编译时也会对成员变量进行对齐处理。可以通过#pragma pack()或者编译器的编译选项来控制它们的成员变量按多少字节对齐,或者关闭对齐。 一个比较有意思的例子来说明各种变量在内存中的位置: #include <stdio.h> #include <stdlib.h> int nGlobal = 100; int main( void ) { char *pLocalString1 = "LocalString1"; const char *pLocalString2 = "LocalString2"; static int nLocalStatic = 100; int nLocal = 1; const int nLocalConst = 20; int *pNew = new int [5]; char *pMalloc = (char *)malloc(1); printf( "global variable:0x%x\n", &nGlobal ); printf( "static variable:0x%x\n", &nLocalStatic ); printf( "local expression1:0x%x\n", pLocalString1 ); printf( "local expression2(const):0x%x\n", pLocalString2 ); printf( "new:0x%x\n", pNew ); printf( "malloc:0x%x\n", pMalloc ); printf( "Local pointer(pNew):0x%x\n", &pNew ); printf( "Local pointer(pLocalString2):0x%x\n", &pLocalString2 ); printf( "Local pointer(pLocalString1):0x%x\n", &pLocalString1 ); printf( "Local variable(nLocal):0x%x\n", &nLocal ); printf( "Local pointer(pMalloc):0x%x\n", &pMalloc ); printf( "Local const variable(nLocalConst):0x%x\n", &nLocalConst ); return 0; } 在我的机子上(XP pro,VC++6.0)运行结果: global variable:0x425a30 static variable:0x425a34 local expression1:0x4231b4 local expression2(const):0x4231a4 new:0x3807a8 malloc:0x3807f0 Local pointer(pNew):0x12ff6c Local pointer(pLocalString2):0x12ff78 Local pointer(pLocalString1):0x12ff7c Local variable(nLocal):0x12ff74 Local pointer(pMalloc):0x12ff68 Local const variable(nLocalConst):0x12ff70 其他内容跟书上说的大同小异,但是变量间内存的位置跟书上不太一样,为了直观看一下,我自己画了一个示意图来显示各个变量存储位置间的关系。让我不解的是,关于堆,书上new得到的内存地址为0x372aa8,malloc得到的内存地址为0x372ac8,二者刚好差32个字节,也符合堆分配的16字节对齐机制。但是为什么我运行起来,两者间间隔72个字节呢?中间这40个字节用做什么了呢?而我机子上的int的的确确是4个字节啊!试了一下,如果pNew改成1~3个int,malloc得到的内存地址为0x3807e0,差56个字节,太奇怪了。内存碎片?
内存泄漏 堆上的内存相较于栈的一个优势是可以动态管理数据,链表就是一个例子。另外,栈的大小有限制(由编译器决定),而堆的大小一般只受限于系统有效的虚拟内存的大小,因此占用内存较多的对象和数据只能分配到堆上。但是堆上的内存容易引起内存泄漏。上面的程序中,main结束后,pNew和pMalloc会被自动释放,而它们指向的内存是堆上的,不会被自动释放,因此就造成内存泄漏。写程序中要养成好习惯,对内存回收要特别敏感。malloc要用free来释放,new要用delete来释放。。 上面讨论了堆和栈在大小、数据管理和内存安全方面的优劣,从效率角度来讲,栈的内存由系统自动分配,有相应的指令进行压栈出栈操作,所以效率比较高。而申请堆上的内存时,系统需要按一定算法寻找合适大小的空闲堆,并修改相应的维护链表,效率比较低。而且栈上分配的内存空间是连续的,不会产生内存碎片。而堆容易造成内存碎片,内存使用效率降低。 |
C++应用程序性能优化笔记(一)——数据的存储区域
最新推荐文章于 2022-04-10 22:17:10 发布