【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_freeend_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_freeend_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 位置的链表都为空了。

在这里插入图片描述

最终内存分配失败

在这里插入图片描述

  • 此时操作系统的内存耗尽,无法获取新的内存。而且也没有可用的已经获得的内存链表。最终导致客户端内存分配失败。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章