前言

第一级是直接调用malloc分配空间, 调用free释放空间, 第二级三就是建立一个内存池, 小于128字节的申请都直接在内存池申请, 不直接调用mallocfree. 本节分析第二级空间配置器, STL将第二级配置器设置为默认的配置器, 所以只要一次申请的空间不超过128字节就默认在内存池中申请空间, 超过才会调用第一级配置器.

第二级配置器

首先先来介绍3个常量.

  1. __ALIGN : 以8字节进行对齐
  2. __MAX_BYTES : 二级分配器最大分配的内存大小
  3. __NFREELISTS : 128字节能分配的的链表个数, 并且从每个链表保存的内存大小都是8的倍数, 而且都比前一个大8字节, 也就是分别是8, 16, 32...128字节
// 二级配置器
enum {__ALIGN = 8}; // 设置对齐要求. 对齐为8字节, 没有8字节自动补齐
enum {__MAX_BYTES = 128}; // 第二级配置器的最大一次性申请大小, 大于128就直接调用第一级配置器
enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; // 链表个数, 分别代表8, 16, 32....字节的链表

再介绍一个宏操作, 这是进行对齐操作, 将不满8的倍数的填充成8的倍数.

static size_t FREELIST_INDEX(size_t bytes) \
{\
return (((bytes) + ALIGN-1) / __ALIGN - 1);\
}

从allocate先切入分析

  1. 先判断申请的字节大小是不是大于128字节, 是, 则交给第一级配置器来处理. 否, 继续往下执行
  2. 找到分配的地址对齐后分配的是第几个大小的链表.
  3. 获得该链表指向的首地址, 如果链表没有多余的内存, 就先填充链表.
  4. 返回链表的首地址, 和一块能容纳一个对象的内存, 并更新链表的首地址
static void * allocate(size_t n)
{
obj * __VOLATILE * my_free_list;
obj * __RESTRICT result; if (n > (size_t) __MAX_BYTES)
{
return(malloc_alloc::allocate(n));
}
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
if (result == 0) // 没有多余的内存, 就先填充链表.
{
void *r = refill(ROUND_UP(n));
return r;
}
*my_free_list = result -> free_list_link;
return (result);
};

refill内存填充.

  1. 向内存池申请空间的起始地址
  2. 如果只申请到一个对象的大小, 就直接返回一个内存的大小, 如果有更多的内存, 就继续执行
  3. 从第二个块内存开始, 把从内存池里面分配的内存用链表给串起来, 并返回一个块内存的地址给用户
// 内存填充
template <bool threads, int inst>
void* __default_alloc_template<threads, inst>::refill(size_t n)
{
int nobjs = 20;
char * chunk = chunk_alloc(n, nobjs); // 向内存池申请空间的起始地址
obj * __VOLATILE * my_free_list;
obj * result;
obj * current_obj, * next_obj;
int i; // 如果只申请到一个对象的大小, 就直接返回一个内存的大小
if (1 == nobjs) return(chunk);
my_free_list = free_list + FREELIST_INDEX(n); // 申请的大小不只一个对象的大小的时候
result = (obj *)chunk;
// my_free_list指向内存池返回的地址的下一个对齐后的地址
*my_free_list = next_obj = (obj *)(chunk + n);
// 这里从第二个开始的原因主要是第一块地址返回给了用户, 现在需要把从内存池里面分配的内存用链表给串起来
for (i = 1; ; i++)
{
current_obj = next_obj;
next_obj = (obj *)((char *)next_obj + n);
if (nobjs - 1 == i)
{
current_obj -> free_list_link = 0;
break;
}
else
{
current_obj -> free_list_link = next_obj;
}
}
return(result);
}

再从deallocate结束

  1. 释放的内存大于128字节直接调用一级配置器进行释放
  2. 将内存直接还给对应大小的链表就行了, 并不用直接释放内存, 以便后面分配内存的时候快速.
static void deallocate(void *p, size_t n)
{
obj *q = (obj *)p;
obj * __VOLATILE * my_free_list; // 释放的内存大于128字节直接调用一级配置器进行释放
if (n > (size_t) __MAX_BYTES)
{
malloc_alloc::deallocate(p, n);
return;
}
my_free_list = free_list + FREELIST_INDEX(n);
q -> free_list_link = *my_free_list;
*my_free_list = q;
}

统一的接口

定义符合STL规格的配置器接口, 不管是一级配置器还是二级配置器都是使用这个接口进行分配的

// 定义符合STL规格的配置器接口, 不管是一级配置器还是二级配置器都是使用这个接口进行分配的
template<class T, class Alloc>
class simple_alloc {
public:
static T *allocate(size_t n)
{ return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); }
static T *allocate(void)
{ return (T*) Alloc::allocate(sizeof (T)); }
static void deallocate(T *p, size_t n)
{ if (0 != n) Alloc::deallocate(p, n * sizeof (T)); }
static void deallocate(T *p)
{ Alloc::deallocate(p, sizeof (T)); }
};

总结

用链表来保存不同字节大小的内存块, 就很容易的进行维护, 而且每次的内存分配都直接可以从链表或者内存池中获得, 提升了我们申请内存的效率, 毕竟每次调用malloc和free效率是很低的, 特别是很小内存的时候.

STL默认的就是第二级配置器, 它会自动判断我们使用哪一个配置器.

STL源码分析之第二级配置器的更多相关文章

  1. STL源码分析之第一级配置器

    前言 上一节我们分析了空间配置器对new的配置, 而STL将空间配置器分为了两级, 第一级是直接调用malloc分配空间, 调用free释放空间, 第二级三就是建立一个内存池, 小于128字节的申请都 ...

  2. 《STL源码剖析》chapter2空间配置器allocator

    为什么不说allocator是内存配置器而说是空间配置器,因为空间不一定是内存,也可以是磁盘或其他辅助介质.是的,你可以写一个allocator,直接向硬盘取空间.sgi stl提供的配置器,配置的对 ...

  3. STL源码分析之内存池

    前言 上一节只分析了第二级配置器是由多个链表来存放相同内存大小, 当没有空间的时候就向内存池索取就行了, 却没有具体分析内存池是怎么保存空间的, 是不是内存池真的有用不完的内存, 本节我们就具体来分析 ...

  4. STL源码分析读书笔记--第二章--空间配置器(allocator)

    声明:侯捷先生的STL源码剖析第二章个人感觉讲得蛮乱的,而且跟第三章有关,建议看完第三章再看第二章,网上有人上传了一篇读书笔记,觉得这个读书笔记的内容和编排还不错,我的这篇总结基本就延续了该读书笔记的 ...

  5. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  6. stl源码分析之allocator

    allocator封装了stl标准程序库的内存管理系统,标准库的string,容器,算法和部分iostream都是通过allocator分配和释放内存的.标准库的组件有一个参数指定使用的allocat ...

  7. STL源码分析《4》----Traits技术

    在 STL 源码中,到处可见 Traits 的身影,其实 Traits 不是一种语法,更确切地说是一种技术. STL库中,有一个函数叫做 advance, 用来将某个迭代器(具有指针行为的一种 cla ...

  8. STL源码分析《3》----辅助空间不足时,如何进行归并排序

    两个连在一起的序列 [first, middle) 和 [middle, last) 都已经排序, 归并排序最核心的算法就是 将 [first, middle) 和 [middle, last) 在  ...

  9. STL 源码分析《1》---- list 归并排序的 迭代版本, 神奇的 STL list sort

    最近在看 侯捷的 STL源码分析,发现了以下的这个list 排序算法,乍眼看去,实在难以看出它是归并排序. 平常大家写归并排序,通常写的是 递归版本..为了效率的考虑,STL库 给出了如下的 归并排序 ...

随机推荐

  1. Codevs 1744 格子染色==BZOJ 1296 粉刷匠

    1744 格子染色  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题解  查看运行结果     题目描述 Description 有 n 条木板需要被粉 ...

  2. Lucas模板&快速幂模板

    /* *********************************************** Author :guanjun Created Time :2016/5/20 0:28:36 F ...

  3. 关于Web API中使用ajax发送请求方式的问题

    参见以下文章 http://www.west-wind.com/weblog/posts/2012/May/08/Passing-multiple-POST-parameters-to-Web-API ...

  4. [Jsoi2015]字符串树

    https://www.zybuluo.com/ysner/note/1298148 题面 字符串树本质上还是一棵树,即\(N\)个节点\(N-1\)条边的连通无向无环图,节点 从\(1\)到\(N\ ...

  5. 30. extjs getEl方法 怎么用

    转自:https://blog.csdn.net/evilcry2012/article/details/50586861 2014-10-27 11:57 提问者采纳   getEl = compo ...

  6. jquery easyui 显示和关闭数据加载的遮罩

    $('#yearReportTable').datagrid('loading');//打开等待div $('#yearReportTable').datagrid('loaded');//关闭等待d ...

  7. bzoj 3401: [Usaco2009 Mar]Look Up 仰望【单调栈】

    用单调递减的栈从后往前扫一遍即可 #include<iostream> #include<cstdio> using namespace std; const int N=10 ...

  8. O(1)的快速乘

    那么 有位神仙已经说了O(1)的算法(当然不是我) 这是一种骚操作 直接放代码了啊 inline LL mul(LL a,LL b,LL Mod){ LL lf = a * ( b >> ...

  9. python 操作数据库时遇到的错误

       pymysql.err.ProgrammingError: (1064, "You have an error in your SQL syntax; ch    之前的写法是从文件里 ...

  10. linux学习之路5 系统常用命令

    日期时间 查看设置当前时间 date +%Y--%m--%d 格式化显示时间 -s " "(切换到超级用户)修改时间 hwclock(clock)用以显示硬件时钟时间 命令 cal ...