STL-内存管理

5 篇文章 0 订阅

内存分配

stl有很多种allocator,根据C++的标准,stl的allocator把对象的申请和释放分为四个步骤:

(1) 申请内存空间,对应的函数是allocator::allocate()
(2) 执行构造函数,对应的函数是allocator::construct()
(3) 执行析构函数,对应的函数是allocator::destory()
(4) 释放内存空间,对应的函数是allocator::的allocate()

1._default_alloc_template分配器

这个分配器采用的是内存池的思想,有效的避免了内碎片的问题,内碎片是已经被分配出去了,外碎片是由于太小无法分配出去的空间。
(1)如果申请的空间大于128bytes,就将申请的操作移交给_malloc_alloc_template分配器去处理,如果申请内存大小小于128bytes,就从本分配器维护的内存池中分配内存。
(2)该分配器用空闲链表的方式维护内存池中的空闲空间,空闲链表的形状类似于下图所示:

这里写图片描述

s_free_list是这些空闲分区连的起始地址组成的数组,大小为16,这16个链表中,每个链表中的空闲空间大小都是固定的,第一个链表的空闲大小是8bytes,一次是1624324048566472808896104112120128bytes。
此外有三个指针,s_start_free,s_end_free,s_heap_size,他们分别 指向整个内存池的起始地址,结束地址,和可用空间的大小。

分配内存的过程

1)如果申请的内存空间大于128bytes,就交给第一分配器处理,如果申请的内存空间小于128bytes,就采用当前的第二分配器处理
(2)采用第二分配器,分配器首先将申请内存的大小调制8的倍数n,并根据n找出对应的空闲链表地址_my_free_list
(3)如果空闲链表有可用的空闲块,则将此空闲块返回并跟新_my_free_list,否则进行(4)
(4)到这一步,说明_my_free_list中没有可用的空闲块,分配器就按照下面的上进行分配:
【1】试着用_s_chunk_alloc()申请大小为n*20的内存空间(但是不一定能申请到这么大的内存空间)
【2】如果申请到这么大的空间,则返回给用户,否则进行下一步
【3】将申请到的n*x的内存块取出一个返回给用户,其余的内存块连接到空闲链表_my_free_list

**_s_chunk_alloc()的作用主要是从内存池取空间给空闲链表使用**

_s_chunk_alloc()的具体过程如下:
【1】如果_s_start_free和_S_end_free之间有足够的空间分配n*20的内存空间,更新_s_start_free,并返回申请到的内存空间的起始地址,否则进行下一步
【2】如果_s_start_free和_S_end_free之间有足够的空间分配n的内存空间,则分配整数倍于n的内存空间,更新_s_start_free,由nobj返回这个整数,并返回申请到的内存空间的起始地址,否则进行下一步
【3】到这一步说明内存池连一块大小为n的内存都没有了,此时如果内存池中还有一些内存,那么他的大小一定小于n,将这些内存插入到对应大小的空闲分区链中
【4】调用malloc向运行时库申请大小为(2*n*20+附加量)的内存空间,如果申请成功,则更新s_start_free,s_end_free,s_heap_size,并重新调用_s_chunk_alloc(),否则进行下一步
【5】到这一步,说明malloc申请失败,这时分配器一次遍历16个空闲分区连,只要有一个空闲链,就释放该链中的一个节点,重新调用_s_chunk_alloc()

内存释放的过程

内存释放,他接受两个参数,一个是要指向要释放的内存块的指针p,另一个表示要释放内存块的大小n,如果n大于128bytes,则交给第一分配器处理,否则就找出对应的空闲链表,将区块回收。

2.第一级分配器_malloc_alloc_template
malloc()函数进行内存分配

1) Malloc他有一个将可用的内存快连接成一个长长的列表的所谓空闲链表
(2) 调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需的内存快,然后将内存块一分为二(一块的大小与用户请求的大小相等,另一块大小是剩下的字节),接下来将分配给用户的那块内存传给用户,并将省下的那块返回到连接表上
(3) 调用free函数时,它将用户释放的内存快连接到空闲链上,到最后空闲链上会被切成很多小内存片段,如果这时候用户申请一个大的内存片段,那么空闲链上就可能没有满足用户需求的片段,于是malloc函数请求延时,并在空闲链表上检查内存片段,对它们进行整理,将相邻的小空心块,合并成较大的内存块
(4) Glibc维护的不止一个不定长的内存块链表,而是好几个,每一个这种链表负责一个大小范围,这种做法有效的减少了分配大内存时的遍历开销
(5) Glibc另外的策略就是不只维护一个空闲链表,而是另外在维护一个缓冲链表和一个高速缓冲链表
(6) 在分配的时候首先在高速缓存中查找,失败之后再在空闲链表上查找,如果找到的内存块比较大,那么将切割之后的剩余内存块插入到缓存链表中
(7) 如果空闲链表查找失败,那么就往缓存链表里查找,如果还是没有合适的空闲块,就像内存申请比请求数更大的内存块
(8) 在对内存进行了free调用之后,我们需要做的是,把他们标记为未使用,并且在调用malloc时,我们可以定位到没有被使用的内存块(这就解释了为什么free之后堆上的内存没有被释放)
(9) Malloc申请内存主要是在heap区域分配的,进程所面对的虚拟内存地址空间,都是按页映射到物理内存地址,才能真正使用,受物理存储容器限制,整个堆虚拟内存空间不可能全部映射到实际的物理内存。Linux对堆的管理示意图:

这里写图片描述

Linux维护了一个break指针,这个指针指向堆空间的某个地址,从堆起始地址到break之间的地址空间为映射好的,可以供进程访问,而从break往上,是为映射的地址空间,如果访问这段空间,则长须会报错。要增加一个进程实际的可用堆的大小,就要把break往高地址移动,Linux通过brk和sbrk系统调用操作break指针
(10)    Linux是按页进行内存映射的,所以如果break的设置没有按页大小对齐,系统实际上会在最后映射一个完整页,从实际已经映射的空间比break指向的地方要大一些,但是使用break地址之后的地址就很危险了

free()函数释放内存的过程

1free函数是将之前用malloc分配的空间还给程序或操作系统,也就是释放内存,让它重新得到自由
(2free释放的指针指向内存,释放内存后把指针指向为空,防止指针在后面又一不小心被解引用
(3free函数的参数非常简单,只有一个参数,只要把指向申请空间的指针传递给free中的参数就完成了释放工作
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值