动态内存管理_C

一、函数介绍

以下四个函数都包含在头文件< stdlib.h >

1.malloc
函数原型:void *malloc(size_t size);
作用:从堆空间申请内存
函数参数:需要申请的空间大小(字节数)
返回值:申请成功则返回一个指向申请到的内存的指针,失败则返回NULL

2.calloc
函数原型:void *calloc(size_t num,size_t size)
作用:从堆空间申请内存,并把申请到的内存初始化为0
函数参数:第一个参数是需要申请多少块内存,第二个参数是每块内存的大小(字节数)
返回值:申请成功则返回一个指向申请到的内存的指针,失败则返回NULL

3.realloc
函数原型:void* realloc(void *ptr,size_t size)
作用:用于修改一个原先已经分配的内存块的大小
函数参数:第一个参数是指向已分配到的内存的指针,第二个参数是重新设定内存的大小(字节数)
返回值:申请成功则返回一个指向申请到的内存的指针,失败则返回NULL
注意:如果relloc函数的第一个参数是NULL,那么它的行为就和malloc相同

4.free
函数原型:void free(void *ptr)
作用:释放从堆空间申请到的内存

二、malloc的实现机理和内存池

从表面上来看,我们每次使用malloc的时候,都是从堆空间“要”了一块空间,好像是系统提供了相应的系统调用,可以用malloc函数使用这个系统调用去申请内存。但是实际上,这样做的性能很差,因为每次申请空间和释放空间都会进行系统调用,而系统调用的开销是比较大的,频繁的堆操作会严重影响程序的性能。

所以这里采用另一种方法,使用malloc的时候,系统从堆上分配了一块足够大的空间,然后以后的内存操作都是在这块空间上执行,由malloc来管理这块空间,当这块空间不足时,再向系统申请,这样就可以避免频繁的系统调用。

分配算法
如果你是个有心人的话,相信你会在很多书上看到这句话:栈内存的分配类似于数据结构中的栈结构,堆内存的分配类似于数据结构中的链表。这句话说的没错,以前堆内存的管理就类似于双向链表。

内存区域

对上面那些内存块的管理采用双向链表的方式,它们通过指针连接。
内存块的结构

从这个图我们还可以的得出的一个结论就是:在使用malloc的时候,尽量一次申请一块足够大的空间。因为如果你频繁申请那些比较小的空间,那么你申请的空间附带的额外开销(额外内存空间)就会很大。

所以整个堆区就会出现下面这种布局:
内存布局
其中free会将连续的空闲块合并为一个大的空闲块(类似操作系统内存管理的伙伴系统)
malloc和free的工作就是对已有内存块的拆分和合并,并没有频繁的向操作系统申请内存,这样就大大提高了内存分配的效率。

但是这样又引入了新的问题:

  • 形成很多内存碎片
  • 频繁分配和释放内存造成链表过长,导致遍历时间增加
  • 容易出现越界读写

针对这些问题,后来的内存管理又提出了内存池的概念
内存池
不管具体的分配算法是怎样的,为了减少系统调用,减少物理内存碎片,malloc() 的整体思想是先向操作系统申请一块大小适当的内存,然后自己管理,这就是内存池。

内存池的研究重点不是向操作系统申请内存,而是对已申请到的内存的管理,这涉及到非常复杂的算法,是一个永远也研究不完的课题,除了C标准库自带的 malloc(),还有一些第三方的实现,比如 Goolge 的 tcmalloc 和 jemalloc。
内存池的详细讲解参考:
内存池技术介绍

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值