【转载:C++内存管理】G2.9 std::alloc 运行模式

在上一篇里面说到,vs 的标准分配器 std::allocator 并没有做内存管理。gnu的历史版本 2.9 中的标准分配器 std::allocator 也没有做内存管理,但是它提供的 extended allocator 中,std::alloc 就是一个做了内存管理的版本。在 gnu 新版本中,__gnu_cxx::__pool_alloc<_Ty> 就是 G2.9 的 std::alloc 的化身。

下面先分析以下 std::alloc 的运行模式,再下一篇分析具体代码。
不过先需要前三篇的基础:

内存管理实例一
内存管理实例二
内存管理实例三

数据结构
在这里插入图片描述

G2.9 std::alloc

一个指针数组 free_list[16],分别负责16条不同大小的 block,以 8 bytes 为间隔。比如 #0 负责 8 bytes 的 block,#3 负责 32 bytes 的 block。当客户端需要 size 大小的内存时,首先被调整到 8 的倍数,然后到 free_list 相应的位置索取。
当用户所索取的 block 大小超过了 free_list 所能提供的最大的大小也就是 128 字节时,会转而调用 malloc 函数。
以用户索取 size = 32 为例。如果 #3 位置上没有分配内存块,那么会一次性分配 20 * size * 2 + ROUNDUP() 这么大的内存块 (以下称为 chunk)。其中前面的 20 * size 大小的内存块会被分割成 20 个 blocks,被串成链表,并且把第一块 block 返回给客户。剩下的 20 * size + ROUND() 大小的 chunk 会被用来当作 memory pool,其中有一个 start_free 和 end_free 指针分别指向这一块内存的首尾位置。这一块 memory pool 为下一次用户索取不同大小 size 的 block 做准备。ROUND() 是一个与当前分配内存总量相关的函数,下面再具体说明。
每一次对内存块进行切割成 block 串成链表,链表的长度不会超过 20。
除了所分配的一整块 chunk 的首尾 block 带有 cookie 之外,其余的都是 cookie free blocks。
实例分析

申请新的内存块

在这里插入图片描述

由于 此时 memory pool 为空,所以利用 malloc 函数,向操作系统索取 32 * 20 * 2 + ROUNDUP (0 >> 4) = 1280 大小的 chunk。将前面的 640 bytes 大小切割成 20 个 block ,串成链表,并将第一个 block 返回给客户。
start_free 和 end_free 指向剩余 640 bytes 相对应的位置。
这里也可以看出,ROUNDUP 函数就是返回 当前已分配的内存总量 / 16 的结果。

在这里插入图片描述

由于此时 memory pool 有640 bytes 的余量,所以直接对这一块 chunk 进行切割。刚好能够切割 10 个 block (这也是为什么前面说,链表的长度最大为 20)。返回第一个 block。
#3 和 # 7 所指向的内存块,在物理上是连续的,但是在逻辑上,是不连续的。
此时累计申请 1280 bytes,memory pool 余量为 0 byte

在这里插入图片描述

由于此时 memory pool 为空,又需要向操作系统索取 96 * 2 * 20 + ROUNDUP (1280 >> 4) = 3920 大小的 chunk。前 1920 串成链表,返回第一个,后 2000 作为 memory pool。
此时累计申请 5200 bytes,memory pool 余量为 2000 byte

客户端连续申请

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

内存碎片的处理

在这里插入图片描述

此时 memory pool 只有 80 bytes ,并不能够分出一个 104 bytes 的 block,此时就产生了内存碎片。对于内存碎片,会将它以一个单位大小的 block 拨给 free_list 相对应的位置。比如再这里,会将这一块 80 bytes 的内存块拨给 #9 的位置。最后再来分配新的内存。

在这里插入图片描述

在这里插入图片描述

此时 memory pool 只有 168 bytes,只能分出 3 个 48 bytes 的 block,剩下的 24 bytes 作为 memory pool。
内存耗尽时

在这里插入图片描述

此时 memory pool 只有 24 bytes ,并不能够分出一个 72 bytes 的 block,这个时候又产生了内存碎片。相同的,首先将这 24 bytes 的内存块拨到 #2 位置。然后再来索取新的内存块。
假设此时由于内存耗尽,操作系统无法一次性给予 72 * 20 * 2 + ROUNDUP(9688 >> 4) 这么大的内存块。这个时候,它就会向距离它最近的,比 block size 大的内存链表中索取一块,从中切出 72 byte 的大小。这里 #9 位置正好有一个 block ,它就会被转接到 #8 位置上,原来 #9 的链表断开。其中前 72 bytes 被返回给客户,最后剩下的 8 bytes 作为 memory pool。最后的结果就是 #8 #9 位置的链表都为空了。

在这里插入图片描述

最终内存分配失败
在这里插入图片描述

此时操作系统的内存耗尽,无法获取新的内存。而且也没有可用的已经获得的内存链表。最终导致客户端内存分配失败。

转载自原博文地址,点击跳转。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值