6.28 内存分配/管理 学习总结

一、通过程序验证系统内存分配机制

1、实验:

两个char 指针,连续先后在内存中各申请100、102个字节, 它们实际上会占用多少字节的内存空间?

如果两个char 指针,连续先后在内存中各申请200个字节, 它们实际上会占用多少字节的内存空间?

2、程序以及运行结果:

#include<iostream>
#include <unistd.h>
#include <malloc.h>
using namespace std;

int main(){
    char* p1 = (char *)malloc(100);
    char* p2 = (char *)malloc(102);
    cout << malloc_usable_size(p1) << endl;
    cout << malloc_usable_size(p2) << endl; 
    cout << static_cast<void *>(p1) << endl;   
    cout << static_cast<void *>(p2) << endl;  
    free(p1);
    free(p2);
    return 0;
}

运行出的两个指针的实际占用大小都是104字节。

通过进一步的实验发现分配的内存大小和实际占用内存有如下关系:

分配的内存大小实际占用内存大小
0 ~ 2424
25 ~ 4040
41 ~ 5656
......
89~104104
......
#include<iostream>
#include <unistd.h>
#include <malloc.h>
using namespace std;

int main(){
    char* p1 = (char *)malloc(200); 
    printf("p1 = %p\n", p1);
    char* p2 = (char *)malloc(200); 
    printf("p2 = %p\n", p2);

    printf("p1内存起始地址:%x\n", p1);
    printf("p2内存起始地址:%x\n", p2);

    printf("使用cat /proc/%d/maps查看内存分配\n",getpid()); 
    getchar();

    free(p1);
    p1 = NULL;
    free(p2);
    p2 = NULL;
    return 0;
}

实际上,除了200字节的可用空间外,还需要耗费内存控制块的空间。malloc 返回给用户态的内存起始地址比进程的多了 16 字节。这个多出来的 16 字节就是保存了该内存块的描述信息。

3、实验结果分析

        通过上述实验结果可以得出,在64位系统下,我们申请的可用内存的大小会是16x + 8,分配的内存大小在每个区间内会对应相同的一个实际占用内存大小。那么这样内存分配的目的是什么呢?通过查阅相关资料得出,这样做是为了使得内存对齐,从而提升效率。除了可用空间外,还需要耗费内存控制块的空间。所以malloc实际分配的内存大小会大于我们所需要的大小,并且由字节对齐和内存控制块两方面决定。

二、内存对齐

1、前言

        内存用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。内存是外存和CPU进行沟通的桥梁,是计算机中非常重要的组成部分。

        执行一个程序时,先由输入设备向CPU发出操作指令,CPU接收到操作指令后,硬盘中对应的程序就会被直接加载到内存中。然后,CPU 再对内存进行寻址操作,把加载到内存中的指令翻译出来,发送操作信号给操作控制器,实现程序的运行。计算机中程序的运行都是在内存中进行,所以内存的性能的好坏会影响计算机的性能。

2、内存对齐是什么?为什么要进行内存对齐?

        内存对齐就是把各种类型数据按照一定的规则在空间上排列,而不是按照顺序一个接一个的排放,这种就称为内存对齐,内存对齐是指首地址对齐。原因如下:

(1)性能方面考虑:

       CPU每次访问内存是以字长为单位访问的,所以应该内存对齐。如果访问未对齐的存,CPU需要进行两次访问操作。 因此,内存对齐可以提升性能。

        从硬件角度分析,因为内存条就是这么设计的,内存颗粒chip里的bank是并行的。一个内存是由许多chip组成,而每个chip内部是由8个bank组成。bank也就是一个二维矩阵,矩阵里的一个元素保存一个字节。

        在应用程序中,内存中连续的8个字节看似是连续的,但是在物理上并不是连续的,这是由于8个bank实际上是可以并行工作的。内存对其的情况下,工作一次,把得到的数据拼起来,就能得到内存中地址连续的8个字节,从而提高了IO效率。

(2)平台方面考虑:

        有些硬件平台上的CPU可以访问任意地址上的任意数据的,但有些只能在特定地址访问数据,否则会抛出硬件异常。因为不同的硬件平台具有一定的差别,因此,在编译阶段,把分配的内存对齐,就具有了可移植性了。

3、如果内存不对齐,会发生什么?

        未对齐的内存访问在不同的体系结构中会有不同的效果:有些计算机可以透明地处理未对齐访问,但会大大降低效率;有一些计算机可能会产生异常,并且可用调用一个异常处理程序改正;但有些计算机产生异常,但是不能校正错误;还有一些计算机没有办法处理未对齐访问,而直接读取不正确的内存地址,导致代码漏洞,并且难以发现。

三、内存管理

1、malloc

        malloc申请内存的时候,分配的是虚拟内存,会有两种方法向操作系统申请内存:

(1)通过brk()系统调用从堆分配内存

(2)通过mmap()系统调用在文件映射区分配内存

        malloc() 源码里默认定义了一个阈值:(不同的 glibc 版本定义的阈值也是不同的)

  • 如果用户分配的内存小于 128 KB,则通过 brk() 申请内存;

  • 如果用户分配的内存大于 128 KB,则通过 mmap() 申请内存;

        malloc 函数的实质有一个将可用的内存块连接为一个链表的空闲链表,有时候也用双向链表实现。每一个内存块都有一个首部称为内存控制块mem_control_block,该MCB中记录了该内存块的一些信息,例如下一个分配块的指针、当前分配块的长度、当前分配块是否可用。

typedef struct mem_control_block
{
    size_t size;  //本块大小
    bool free;  //空闲状态
    struct mem_control_block *next;  //后块指针
}MCB;
MCB *g_top = NULL;  //栈顶指针

        malloc分配时会去搜索空闲链表,为了保证分配给程序的内存的连续性,根据内存匹配原则,找到一个能满足所需空间的空闲内存块,然后把该部分空间分配出去,并且返回这部分空间的起始指针。malloc返回的指针是可用空间的起始指针,也就是跟在首部后面的地址,上述分配块的首部对于程序是不可见的。如果不能找到满足条件的内存块,就会向操作系统申请扩展堆内存,操作系统会分配新的足量内存并将其控制块压入链表栈。

        这相当于用到了池化技术,实现了一个内存池。先向操作系统申请了适当内存空间,然后自己进行管理。如果不够满足实际需求,再向操作系统申请。不需要每次malloc都进行系统调用,那样开销比较大,会影响系统的性能。

        系统函数brk()和sbrk()被用来调节分配给进程的数据段的内存量。 brk和sbrk底层维护了一个堆上的指针,以增量的方式管理动态内存(堆)。通常,这些函数是由一个更大的内存管理库函数(如malloc)调用的。sbrk(x) 函数,x是申请空间大小,0不申请空间,大于0申请空间,小于0 释放空间。而brk(addr),需要将堆顶指针向后移动到哪个地址addr。移动的空间大小,就是申请的空间大小。

2、free

    free会将内存块重新插入到空闲链表中,并且修改内存头部的空闲状态。free只接受一个指针,却可以释放恰当大小的内存,这是因为在分配的区域的首部保存了该区域的大小。

        malloc 申请内存的时候,会有两种方式向操作系统申请堆内存:1、通过 brk() 系统调用从堆分配内存;2、通过 mmap() 系统调用在文件映射区域分配内存。采用第一种方式申请的内存,堆内存还存在,没有归还给操作系统,而是放回内存池里;采用第二种方式申请的内存会归还给操作系统。

参考文章:

详解内存对齐_Sunshine-松的博客-CSDN博客

带你深入理解内存对齐最底层原理 - 知乎

内存管理(六):一文搞懂malloc、free实现原理 - 知乎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值