494-SGI STL二级空间配置器(_S_chunk_alloc函数和内存池总结)

引言

今天,我们来分析_S_chunk_alloc函数
在这里插入图片描述
在这里插入图片描述
接下来我们看_S_chunk_alloc函数对具体内存块的分配
nobjs都是20,这是固定的数字。然后还传入我们申请的字节数。
也就是该chunk的个数和该chunk的大小
然后把chunk块的内存池起始地址返回

我们进入到_S_chunk_alloc函数

在这里插入图片描述

正常流程的内存分配

我们一步一步来解析下去
__total_bytes计算内存池的总字节数
__bytes_left就是剩余的字节数
在这里插入图片描述
因为初始化都是0,所以end-start=0
然后就到else代码段里执行
S_heap_size是0,0右移4位还是0

在这里插入图片描述
然后执行malloc了,按字节数开辟内存( * 2),对于8字节的大小来说,开辟了40个chunk块

然后执行:开辟内存
在这里插入图片描述
在这里插入图片描述
如果_S_start_free为0,就是开辟内存失败。
在这里插入图片描述
我们先假设开辟内存成功了。然后执行:
在这里插入图片描述
_S_heap_size+=320,现在就是320了。(因为我们假设申请8字节的)
然后让_S_end_free到末尾
在这里插入图片描述
然后执行return,又是对当前函数的递归调用。
因为一刚开始,相应大小字节的chunk块根本就没有,就得首先分配chunk块内存池,分配好内存池之后,然后又启动当前函数,现在就是8字节的chunk块内存池已经分配好了。
在这里插入图片描述
现在__bytes_left=320

320>160,进入这个if语句:
在这里插入图片描述
在这里插入图片描述
如图所示:
在这里插入图片描述
相当于把前面20块chunk使用起来,后20块chunk备用。
最后把第一个chunk地址返回会去。

全局调用流程梳理看看:
在这里插入图片描述

deallocate内存归还释放函数

在这里插入图片描述
传入的第一个 参数就是要归还chunk块的起始地址,第二个参数是归还的chunk的字节数

如果归还的字节数大于128字节,那么当时分配的时候就是通过一级空间配置器malloc来分配,没有从内存池来分配,所以释放的时候也是通过free来释放。
在这里插入图片描述

else ,就是小于等于128字节,当时也是从内存池分配出去的,所以,归还的时候,也应该归还到内存池中。
首先得定位到静态链表中的第一维的元素下标位置
在这里插入图片描述
举个例子。如下图,假如前2个chunk块都分配出去了。
在这里插入图片描述
现在要归还。首先__q指向归还的节点,
在这里插入图片描述
然后加锁,
在这里插入图片描述
第1句做的事情如图所示:
在这里插入图片描述

第2句做的事情如图所示:
在这里插入图片描述
全局梳理看看:
在这里插入图片描述

继续讲解_S_chunk_alloc内存分配

在这里插入图片描述
假设它把前20个chunk块分配完了,第20chunk块的头部是空指针,如果把第20个chunk块分配出去了,相当于把第20块的头部的空指针赋值给了第一维数组的元素值。

然后再去分配的话,result还是0了:
在这里插入图片描述
160= =160,进入if
在这里插入图片描述
就是后20个chunk块的首地址返回回去。
在这里插入图片描述

如果又都用完了,前面都是0,if和else进不去,然后又进入else
在这里插入图片描述
40+20=60 开辟60个chunk块。因为使用的比较多,重新开辟的更多一点。

我们再看其他情况

如果现在分配8个字节,
在这里插入图片描述
后20个chunk块也是按8字节计算的。
在这里插入图片描述
假如说,我们申请了几个8字节的chunk,现在要申请16字节的,
在这里插入图片描述
16*20=320
__total_bytes就是320,此时,__bytes_left=160字节。
160<320
不进入if语句
160>16(size)
进入else if
在这里插入图片描述
它在分配其他字节数的chunk。并没有直接分配,而是在看,在之前分配过的备用的内存池
在这里插入图片描述
160>16(size)
进入
在这里插入图片描述
160/16=10
nobjs成10了
也就是说,刚才分配的时候,是按8字节分配的,现在一除,
在这里插入图片描述
现在变成10个chunk,一个chunk16个字节。
在这里插入图片描述
然后return result
然后会调用refill,把节点连起来。

在这里插入图片描述

我们再举一个例子,假如说,我们申请了几个8字节的chunk,现在要申请128字节的chunk,
很明显,160>128
进入

在这里插入图片描述
160/128=1
nobjs就是1了
在这里插入图片描述
在这里插入图片描述
现在备用内存池还剩余32字节,假如我们现在还想要申请40字节的chunk块,现在40字节大小对应的数组的第一维元素是空值,进入refill函数的chunk_alloc函数
40x20=800字节
备用空间剩余32个字节
32 小于800
32<40

在这里插入图片描述

进入下面的else方法
在这里插入图片描述
320右移4位,总共分配60个40字节的chunk块

越申请越大,因为如下
在这里插入图片描述
32>0,这个32字节也不能浪费
在这里插入图片描述
对齐。直接给了32的位置了。
在这里插入图片描述

在这里插入图片描述

内存分配失败的处理流程

如果malloc开辟失败的话:
在这里插入图片描述
假如我们现在又想申请40字节的chunk块。
原本计划开辟60个40字节的chunk块。

现在进入if语句。
定义了一个i变量和一个二级指针,想遍历数组,obj*p可以访问chunk块的头

在这里插入图片描述
for循环,从40开始,每次加8,遍历,最后到128字节的元素位置。
_p做的事情就是访问相应一维数组元素的内容(访问的是对应的链表)。
内存分配失败,证明系统的内存不够用,所以不用遍历40前面的元素,因为也放不了40,所以直接从40开始向后遍历,
_p到40的位置,是空的,假设我们在48的元素位有开辟内存,_p到48的位置。

在这里插入图片描述
系统没内存申请给40的了,就看它后面的元素有没有chunk块,有就让给它。
48的位置有!!!
在这里插入图片描述
在这里插入图片描述
然后return 进行递归调用
48>40
在这里插入图片描述
取40字节出来。

在这里插入图片描述
将来这个40字节的chunk也就归还到40的名下。

等下次要申请16字节,16>8,不够用,要重新分配,然后看:
在这里插入图片描述

现在备用的内存池空闲剩余8个字节,最后把这个8字节接到8字节的chunk块下面
使用得干干净净!
在这里插入图片描述
如果一直遍历到128字节的chunk块,都没有剩余的,_p为空,让malloc申请。
在这里插入图片描述

最后的问题

我们继续看看:
在这里插入图片描述
在这里插入图片描述
刚才分配失败,现在马上接着分配malloc会不会失败
有可能失败有可能成功,因为多线程,其他线程有可能释放内存了。

成功了result不为空,直接返回。如果失败了,调用_S_oom_malloc
我们进去_S_oom_malloc看看
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
有一个函数指针,指向返回值是void不带形参的函数。

底层开辟内存失败的话,我们可以设置回调函数,它会帮我们调用回调函数,回调函数就是写可以释放资源的代码,让系统调用回调函数释放一些空间。
在这里插入图片描述
如果用户没有设置回调函数,就抛出new操作开辟失败。
如果用户有预先设置回调函数,就会调用用户的回调函数(开发者预估到的预先节约资源的方法)
然后系统再启动malloc。如果还是开辟失败,就再调用回调函数,以此类推,直到调用成功。

如果用户想要预置回调函数,就要考虑清楚这个回调函数真的可以释放出有效的内存空间资源吗?否则就是死循环。

开辟成功后,然后就返回到_S_chunk_alloc函数
在这里插入图片描述

借鉴的地方

在这里插入图片描述

实现的优点

在这里插入图片描述

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林林林ZEYU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值