C++应用程序性能优化笔记(一)——数据的存储区域

5种程序内存区:全局/静态数据区、常量数据区、代码区、栈、堆

全局/静态数据区存储全局变量、全局静态变量、局部静态变量。类的静态成员变量也存储在这个区域,且只有一份拷贝,所有对象共享。可以自己编程写个有静态成员变量的类,然后看看不同对象中这个变量的内存地址,是一样的。

常量数据区存储常量字符串,字符串常量存储的区域不可修改。printf()函数的格式串也是存储于这个区域。常量存储区是4字节对齐的。

全局/静态数据区和常量数据区是在程序编译阶段已分配好,整个程序运行过程中始终存在的。

代码区存储程序代码

栈存储自动变量或局部变量,函数参数等

堆是由用户程序控制的存储区,存储动态产生的数据,通过newmalloc获得的内存是堆得内存,堆上分配内存时按16个字节对齐

上面所述的几个区域,对于C++的对象模型同样适用。即,一个对象的存储位置和它声明的类型有关,声明为全局变量,则存储在全局/静态数据区,通过new来创建,则存储在堆区域。

内存对齐

内存对齐,即分配时按对齐字节数来分配,譬如用new申请5int,占20个字节,但由于堆是按16个字节对齐的,所以这5int实际占32个字节的内存。这种方式虽然浪费一些内存,但是CPU在对齐方式下运行比较快,所以有益于程序性能。另外,stuctuninonclass在编译时也会对成员变量进行对齐处理。可以通过#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 proVC++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得到的内存地址为0x372aa8malloc得到的内存地址为0x372ac8,二者刚好差32个字节,也符合堆分配的16字节对齐机制。但是为什么我运行起来,两者间间隔72个字节呢?中间这40个字节用做什么了呢?而我机子上的int的的确确是4个字节啊!试了一下,如果pNew改成1~3intmalloc得到的内存地址为0x3807e0,差56个字节,太奇怪了。内存碎片?



内存泄漏

堆上的内存相较于栈的一个优势是可以动态管理数据,链表就是一个例子。另外,栈的大小有限制(由编译器决定),而堆的大小一般只受限于系统有效的虚拟内存的大小,因此占用内存较多的对象和数据只能分配到堆上。但是堆上的内存容易引起内存泄漏。上面的程序中,main结束后,pNewpMalloc会被自动释放,而它们指向的内存是堆上的,不会被自动释放,因此就造成内存泄漏。写程序中要养成好习惯,对内存回收要特别敏感。malloc要用free来释放,new要用delete来释放。。

上面讨论了堆和栈在大小、数据管理和内存安全方面的优劣,从效率角度来讲,栈的内存由系统自动分配,有相应的指令进行压栈出栈操作,所以效率比较高。而申请堆上的内存时,系统需要按一定算法寻找合适大小的空闲堆,并修改相应的维护链表,效率比较低。而且栈上分配的内存空间是连续的,不会产生内存碎片。而堆容易造成内存碎片,内存使用效率降低。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值