C++ 内存管理(二)----std::alloc

本文详细探讨了C++中std::alloc的内存管理机制,包括其基本结构、工作流程和源码剖析。std::alloc维护16种大小的内存池,服务8到128字节的对象,超过范围则使用malloc。它为每个容器分配内存时,会调整对象大小为8的倍数,并使用内部free list和战备池。当内存不足时,会尝试从战备池中分配,否则调用malloc。此外,文章还讨论了std::alloc如何处理碎片和内存回收,以及其与malloc的交互。
摘要由CSDN通过智能技术生成

一、std::alloc运行模式(G2.9)

- 基本结构

前面中的per-class allocator的四个版本,实现操作都是每个class里面只有专属于它的allocator,然后其里头也只维护一条free list,专门为其服务。

而std::alloc里的操作是,先创出一个可放置16种大小【以8为倍数关系】的总记录链表,即8~128的16种大小。超出这个大小范围的则std::alloc不为其服务分配内存【每一大段带cookie,小段共享所处大段的cookie】,而是调用malloc去分配内存【每个内存都带cookie】。
在这里插入图片描述
#0记录大小为8的数据类型,
#1记录大小为16的数据类型 …
#15记录大小为128的数据类型,
如果此时需在容器vector中放入一个类型大小为30的元素,则编译器会将其大小调节为8的倍数即调整到32而放入 #3 所malloc出的一大段free list的第一小片中,且此时free list1的头指针向下移一位,则剩下19小段是空的。而在这 #3下所malloc分配得的一大块内存中是带有头尾两个cookie的,然后其实际生成大小为32 * 20 * 2,前20个用来存放32大小的元素数据,而后一半则作为战备池提供后续放置元素使用。
比如,继续在容器list中放入一个类型大小为63的元素,则编译器会将其大小调节为8的倍数即调整到64而放入 #7 中,但 #7下并无挂free list,所以跑到前面 #3 提供的战备池去分配空间。此时战备池大小为32 * 20 =640,所以放置64大小的元素时,可以分成640/64=10块,则此时战备池划分为10小段,第一段已用来放置元素对象,free list2的指针向下移一位,且剩下9小段是空的。
若此时容器set需要放入一个类型大小为256的元素,则此时已不属于std::alloc的服务范围,则调用的是malloc去分配内存空间

  • embedded pointers(内嵌指针)

在这里插入图片描述当容器开始在free list中放置元素值时,就会覆盖掉整小片,此时指针信息已经覆盖掉了,而且指向free list头端的指针下移一位跳到下一个空的位置。
这里指向空表free list头端的指针是嵌属于一小片空间的一部分,因而不占用额外的内存空间!

- 运行图示过程

注意:std::alloc的客户一般都是容器,因为用户自己调用的话,需要记住自己在调用时所分配的内存大小,因为在回收时需对应返回已分配的内存大小参数。而容器由于其内部的元素类型大小是一致的,而且其第一个传入参数即为sizeof(容器具体元素的类型),所以容器在调用std::alloc分配内存时不用担心遗忘内存大小而导致后续释放内存时出错!

设置系统heap最大为10000:
在这里插入图片描述
一开始,容器申请放置32bytes的元素,则应放置于#(32/8-1)即#3中,而#3中pool【战备池】为空,所以调用malloc创建出带头尾两个cookie的一大段内存空间,
申请量为 32 * 20 * 2 + RoundUp(0>>4) = 1280:

32 为按所放置容器的元素的大小而设定,20 * 2则是表示每个最多需要切分成20小段,前半段用于存放操作,而后半段则当作pool【战备池】为后续放置提供操作空间。
RoundUp(0>>4) 表示为上一次累计申请量除以16所得的大小【还要调整为8的倍数】即0/8=0。
因而所得累计申请量为1280,且除去容器所分配的内存大小32 * 20 = 640,
最终pool大小为:1280 - 640 = 640。

然后将所分配出来的一大段内存的前半段【即除去pool以后所剩的】的free list的第一个位置返回给容器,让其放置元素对象,然后free list的头指针下移一位,则此时空白free list剩余19小段。
此时,pool大小为640。

在这里插入图片描述
容器申请放置64bytes的元素,则应放置于#(64/8-1)即#7中,而#7中为空,且pool【战备池】有余量,所以从pool【战备池】中填充(若此时pool没有余量则需调用malloc再重新分配一大块内存)。所以其从pool上切割出1~20块来挂上#7中的free list区块【实际此#7的free list是紧连着#3的free list后面的】。
pool可划分为640/64=10块,刚好使用完pool的内存大小。
此时的累计申请量仍为1280,而pool剩余大小则为0。
在这里插入图片描述容器申请放置96bytes的元素,则应放置于#(96/8-1)即#11中,而#11中为空,且pool【战备池】无余量,所以调用malloc创建出带头尾两个cookie的一大段内存空间,
申请量为 96 * 20 * 2 + RoundUp(1280>>4) = 5200:

96 为按所放置容器的元素的大小而设定,20 * 2则是表示每个最多需要切分成20小段,前半段用于存放操作,而后半段则当作pool【战备池】为后续放置提供操作空间。
RoundUp(1280>>4) 表示为上一次累计申请量除以16所得的大小【还要调整为8的倍数】即为1280/16=80(已为8的倍数)。
因而所得累计申请量为3840+80+1280=5200,且除去容器所分配的内存大小96 * 20 = 1920,
最终pool大小为:
5200 - 1920 - 1280 = 2000 或者 80(累积量额外分配的) + 1920(新分配的后半段的) = 2000

然后将所新分配出来的一大段内存记录在#11下,且其的前半段【即除去pool以后所剩的】的free list的第一个位置返回给容器,让其放置元素对象,然后free list的头指针下移一位,则此时空白free list剩余19小段。
此时,pool大小为2000。

在这里插入图片描述容器申请放置88bytes的元素,则应放置于#(88/8-1)即#10中,而#10中为空,且pool【战备池】有余量2000,所以从pool【战备池】中填充(若此时pool没有余量则需调用malloc再重新分配一大块内存)。所以其从pool上切割出1~20块来挂上#10中的free list区块【实际此#10的free list是紧连着#11的free list后面的】。
pool可划分为2000/88= 20块(最多只能划分20块),此时pool还剩余量2000 - 88 * 20 = 240。
此时的累计申请量仍为5200,而pool剩余大小则为240。
在这里插入图片描述这次不是在应用端生成新的容器来放置元素,而是在前面已有的容器里面在申请放入3个88bytes的元素,则由#10下的free list取出3个位置并返回给容器从而放入元素对象,则此时free list指向头端的指针最终下移3个位置,剩余空位为16个。
此时的累计申请量仍为5200,而pool剩余大小仍为240。

在这里插入图片描述

容器申请放置8bytes的元素,则应放置于#(8/8-1)即#0中,而#0中为空,且pool【战备池】有余量240,所以从pool【战备池】中填充(若此时pool没有余量则需调用malloc再重新分配一大块内存)。所以其从pool上切割出1~20块来挂上#0中的free list区块【实际此#0的free list是紧连着#10的free list后面的也即#11的free list后面的】。
pool可划分为240/8=20块,pool剩余240 - 8 * 20 = 80。
此时的累计申请量仍为5200,而pool剩余大小则为80。

在这里插入图片描述容器申请放置104bytes的元素,则应放置于#(104/8-1)即#12中,而#12中为空,且pool【战备池】有余量80,但此时pool【战备池】余量已不足放入104大小的元素即不可能分割出片段来放置元素,则此80称为碎片。所以将#0下的pool碎片拨给#(80/8-1)即#9下。然后调用malloc创建出带头尾两个cookie的一大段内存空间,
申请量为 104 * 20 * 2 + RoundUp(5200>>4) = 9688:

104 为按所放置容器的元素的大小而设定,20 * 2则是表示每个最多需要切分成20小段,前半段用于存放操作,而后半段则当作pool【战备池】为后续放置提供操作空间。
RoundUp(5200>>4) 表示为上一次累计申请量除以16所得的大小【还要调整为8的倍数】即5200/16=325->328。
因而所得累计申请量为,且除去容器所分配的内存大小104 * 20 = 2080,
最终pool大小为:9688 - 5200 - 2080 = 2408。

以上为碎片的处理!
在这里插入图片描述
容器申请放置112bytes的元素,则应放置于#(112/8-1)即#13中,而#13中为空,且pool【战备池】有余量2408,所以从pool【战备池】中填充(若此时pool没有余量则需调用malloc再重新分配一大块内存)。所以其从pool上切割出1~20块来挂上#13中的free list区块【实际此#13的free list是紧连着#12的free list后面】。
pool可划分为2408/112=20块,pool剩余2408 - 112 * 20 = 168。
此时的累计申请量仍为9688,而pool剩余大小则为168。

在这里插入图片描述容器申请放置48bytes的元素,则应放置于#(48/8-1)即#5中,而#5中为空,且pool【战备池】有余量168,所以从pool【战备池】中填充(若此时pool没有余量则需调用malloc再重新分配一大块内存)。所以其从pool上切割出1~20块来挂上#13中的free list区块【实际此#5的free list是紧连着#13的free list后面】。
pool可划分为168/48=3块,pool剩余168 - 48 * 3 = 24。
然后返回#5下的free list的第一个位置给容器并放置元素对象,然后free list头部指针往下移一位,最终free list剩余2个空白挂于#5上。
此时的累计申请量仍为9688,而pool剩余大小则为24。

在这里插入图片描述容器申请放置72bytes的元素,则应放置于#(72/8-1)即#8中,而#8中为空,且pool【战备池】有余量24,但此时pool【战备池】余量已不足放入72大小的元素即不可能分割出片段来放置元素,则此24称为碎片。所以将#5下的pool碎片拨给#(24/8-1)即#2下。然后 企图 调用malloc创建出带头尾两个cookie的一大段内存空间,需索取申请量为 72 * 20 * 2 + RoundUp(9688>>4) > 10000 了,则此时索取分配新空间失败:

1.则std::alloc从右边最接近其的#9取资源,#9上有80大小的空间,80/72=1块【pool只能分出一块空白区块给#8】,然后此时#9的free list的指针下移一位指向空了。
2.#8返回对应地址给容器并放置元素对象,#8的指针也下移一位直接跳到pool的头端位置即start_free,即此时#8中已无可用区块!!。
3.此时累计申请量仍为9688,pool剩余大小为 8。

在这里插入图片描述容器申请放置72bytes的元素,则应放置于#(72/8-1)即#8中,但#8中已无可用区块,而pool【战备池】的余量8已不足放入72大小的元素即不可能分割出片段来放置元素,则此8称为碎片。所以将#8下的pool碎片拨给#(8/8-1)即#0下。然后 企图 调用malloc创建出带头尾两个cookie的一大段内存空间,需索取申请量为 72 * 20 * 2 + RoundUp(9688>>4) > 10000 了ÿ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值