文章目录
👉什么是空间配置器👈
空间配置器,顾名思义就是为各个容器高效的管理空间(空间的申请与回收)的,在默默地工作。虽然在常规使用STL时,可能用不到它,但站在学习研究的角度,学习它的实现原理对我们有很大的帮助。注:空间配置器是内存池。
直接向堆区申请内存的有 Windows 下的 VirtualAlloc 和 Linux 下的 brk,这个一般不符合我们的需求。而 malloc 是向堆区申请内存的函数,也本质是一个内存池,可用于整个程序的内存管理。STL 的空间配置器也是内存池,其是专门服务 STL 容器的内存管理,以提高效率。
👉为什么需要空间配置器👈
前面在模拟实现 vector、list、map、unordered_map 等容器时,所有需要空间的地方都是通过 new 申请的,虽然代码可以正常运行,但是有以下不足之处:
- 空间申请与释放需要用户自己管理,容易造成内存泄漏
- 频繁向系统申请小块内存块,容易造成内存碎片
- 频繁向系统申请小块内存,影响程序运行效率
- 直接使用 malloc 与 new 进行申请,每块空间前有额外空间浪费
- 申请空间失败怎么应对
- 代码结构比较混乱,代码复用率不高
- 未考虑线程安全问题
👉SGI-STL 空间配置器实现原理👈
以上提到的几点不足之处,最主要还是:**频繁向系统申请小块内存造成的。**那什么才算是小块内存?SGI-STL 以 128 字节作为小块内存与大块内存的分界线,将空间配置器其分为两级结构,一级空间配置器处理大块内存,二级空间配置器处理小块内存。
一级空间配置器
二级空间配置器
如果申请的内存字节数小于 128 时,就走二级空间配置器。首先,看看该大小的内存对应的桶中有没有内存,如果有就直接返回;如果没有,就去调用 malloc 申请大块内存,并将大块内存切分成小块内存头插到对应的桶中。如果多次申请该大小的内存时,就能够提高效率了。
free_list 的桶和哈希桶的区别:哈希桶挂的是节点,这些节点都是一样的;free_list 的桶挂的是内存,不同的桶挂的内存大小是不一样的。因为一个指针是4个字节 或 8 个字节,而我们只需要让内存的头 4 个或 8 个字节指向下一个内存的地址,最后一块内存的头 4 个或 8 个内存指向空。
假设现在要申请 40 个字节,发现桶没有,就去看大块内存够不够 40 个字节。假设没有且再去用 malloc 申请大块内存也失败了,这时候就会去看后面的桶有没有比 40 大的内存,如果有,就将该内存切分成 40 大小的内存和更小的内存,并将该内存挂在对应的桶上。如果还是没有,那么就抛异常。
STL 的空间配置器是专门服务容器的,假设 list 的一个节点大小为 12 个字节,set 的一个节点为 16 字节。假设 list 尾插了 15 次,那么它就使用了 15 个 16 字节大小的内存(大块内存可以被切分成 20 个 所需空间的小内存)。那么现在还是剩下 5 个 16 字节大小的内存,此时 set 开始 insert,那么这 5 个内存就可以被 set 使用。假设 set insert 3 次后,list 析构了并将其内存挂回了 free_list 的桶上,那么这些内存也就可以被 set 所使用。这也是空间配置器提高效率的做法。
一个进程里面应该只有一个空间配置器,所以空间配置器可以设计成单例模式的类。SGI 的 STL 空间配置器是通过静态成员来间接实现单例模式的。
👉空间配置器的优势👈
- 频繁申请小块内存时,效率高(memory 头文件中的 allocator 就是 STL 的空间配置器,可以和 malloc 进行效率对比)。
- 一定程度上缓解了内存碎片的问题,内存碎片分为外碎片和内碎片
- 外碎片问题是由频繁向系统申请小块内存造成的。频繁向系统小块内存会造成有足够的内存,但是不连续,无法申请大块的内存。
- 内碎片问题是实际申请到的内存多于所需的内存,而多出来的内存用户也无法使用。内存块按一定的对齐规则挂起来管理,就会导致内碎片问题。比如:list 的一个节点需要 12 个字节,系统给了用户 16 个字节,多出来的 4 个字节用户也无法使用。内存池一般都会有内碎片问题,内碎片问题不是很严重。
malloc 和 内存池的对比
注:以下实现的定长内存池是高并发内存池项目中的组件,以后会详细讲解!定长内存池的空间是不释放的,因为大块内存已经被切分了好多次,无法正确释放。但是只要进程是正常结束的,也不要造成内存泄漏问题。进程正常结束,操作系统会回收内存。
// ObjectPool是针对某一类对象的定长内存池
#pragma once
#include <iostream>
#include <vector>
#include <time.h>
// 工程项目中不要将std整个命名空间展开,避免命名污染
using std::cout;
using std::endl;
// 操作系统的控制,如果是Windows系统则包含windows