C++之内存模型(内存区域)

1.C++五种存储区域:

1.1 堆

  • 定义:在内存管理的语境下,指的是动态分配内存的区域。这个堆跟数据结构里的堆不是一回事。这里的内存,被分配之后需要手工释放,否则,就会造成内存泄漏。
  • C++ 标准里一个相关概念是自由存储区,英文是 free store,特指使用 new和 delete来分配和释放内存的区域。一般而言,这是堆的一个子集。new和 delete操作的区域是 free store,malloc和free操作的区域是 heap,但 new和 delete 通常底层使用malloc和 free来实现,所以free store也是heap。
  • 堆通常进行的操作:让内存管理器分配一个某个大小的内存块,让内存管理器释放一个之前分配的内存块。
  • 生长方向:对于堆来说,生长方向是向上的,也就是说向着内存地址增加的方向。

1.2 栈

  • 定义:在内存管理的语境下,指的是函数调用过程中产生的局部变量函数形参的区域。这个栈和数据结构里的栈高度相似,都满足“后进先出”的规则。
  • 栈通常进行的操作:

1)栈上的分配极为简单,移动一下栈指针而已。
2)栈上的释放也极为简单,函数执行结束时移动一下栈指针即可。
3)由于后进先出的执行过程,不可能出现内存碎片。

  • 执行效率:栈区的执行效率高于堆区。
  • 生长方向:对于栈来说,生长方向是向下的,也就是说向着内存地址减小的方向。
  • 函数调用参数入栈顺序:参数的入栈顺序一般是从右向左
  • 指针存放的位置:指针不管指向任何类型,指针自身的位置只可能在栈区和全局静态区,函数内定义(包括形参)在栈区,函数外定义在全局静态区。

1.3 全局静态区

  • 定义:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区,程序结束释放。

1.4 代码区

  • 定义:存放函数体等二进制代码;

1.5 常量区

  • 定义:这是一块比较特殊的存储区,他们里面存放的是const,不允许修改。

2.malloc、calloc、realloc底层实现原理

2.1 malloc底层实现原理

2.1.1 malloc分配简单结论

  • 当开辟的空间小于128K时,调用sbrk函数,malloc的底层实现是系统调用函数brk,其主要移动指针 _enddata(此时的 _enddata指的是Linux地址空间中堆段的末尾地址,不是数据段的末尾地址)。
  • 当开辟的空间大于128K时,mmap系统调用函数来在文件映射区域找一块空间来开辟。
  • 这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。

2.1.2 malloc分配详细过程

2.1.2.1 malloc小于128K的内存,使用brk分配
  • 将_enddata往高地址推(只分配虚拟空间,不对应物理内存,因为没有初始化),第一次读/写数据时,引起内核缺页中断,内核才分配对应的物理内存,然后虚拟地址空间建立映射关系。
  • 当malloc(30K)的时候,malloc函数会调用brk系统调用,将_enddata指针往高地址推30K,就完成虚拟内存分配。事实是:_endata+30K只是完成虚拟地址的分配,这块内存现在还是没有物理页与之对应的,等到进程第一次读写A这块内存的时候,发生缺页中断,这个时候,内核才分配A这块内存对应的物理页。也就是说,如果用malloc分配这块内容,然后从来不访问它,那么对应的物理页是不会被分配的。
  • 如下图所示:当进程调用free(B),B对应的虚拟内存和物理内存都没有释放,因为只有一个_endata指针,如果往回推,那么D这块内存怎么办呢?当然,B这块内存,是可以重用,如果这个时候再来一个40K的请求,那么malloc很可能就把B这块内存返回回去。
2.1.2.2 malloc大于128K的内存,使用mmap分配(munmap释放)
  • 默认情况下,malloc函数分配内存,如果请求内存大于128K,那就不是去推_endata指针了,而是利用mmap系统调用,从文件映射分配一块虚拟内存,这样子做主要是因为:brk分配的内存需要等到高地址内存释放以后才能释放,而mmap分配的内存可以单独释放,也就是说当调用free时虚拟内存和物理内存一起释放。
  • 当进程调用free(D),B和D连接起来,变成一块140K的空闲内存。
  • 当最高地址空间的空闲内存超过128K时,执行内存紧缩操作(trim)。在上一个步骤free的时候,发现最高地址空闲内存超过128K,于是内存紧缩,变成图9所示。

2.2 calloc底层实现原理

  • calloc函数的功能与malloc函数的功能相似,都是从堆分配内存。其函数声明如下:void *calloc(int n,int size);。参数释义:size:单个类型大小;n:申请的个数。
  • 最后申请空间大小为n和size相乘,函数返回值为void型指针。如果执行成功,函数从堆上获得size*n的字节空间,并返回该空间的首地址;如果执行失败,函数返回NULL。该函数与malloc函数的一个显著不同时是,calloc函数得到的内存空间是经过初始化的,其内容全为0。calloc函数适合为数组申请空间,可以将size设置为数组元素的空间长度,将n设置为数组的容量。

2.3 realloc底层实现原理

  • realloc函数的功能比malloc函数和calloc函数的功能更为丰富,可以实现内存分配和内存释放的功能,其函数声明如下:void * realloc(void * p,int size);。参数释义:p为堆上已经存在空间的地址;size为空间的大小。函数详述:指针p必须为指向堆内存空间的指针,即由malloc函数、calloc函数或realloc函数分配空间的指针。realloc函数将指针p指向的内存块的大小改变为size字节。如果size小于或等于p之前指向的空间大小,那么保持原有状态不变。如果size大于原来p之前指向的空间大小,那么系统将重新为p从堆上分配一块大小为n的内存空间。同时,将原来指向空间的内容依次复制到新的内存空间上,p之前指向的空间被释放。relloc函数分配的空间也是未初始化的
  • 注意:使用malloc函数,calloc函数和realloc函数分配的内存空间都要使用free函数或指针参数为NULL的realloc函数来释放。

2.4 分配虚拟内存的细节

  • malloc()在运行期动态分配分配内存,free()释放由其分配的内存。malloc()在分配用户传入的大小的时候,还分配的一个相关的用于管理的额外内存,不过,用户是看不到的。所以,实际的大小 = 用户空间 + 管理空间。在64位系统中,malloc(0)的有效内存大小为24,32位中为12。准确的说是至少是这么多,并且这些内存是可
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值