STL的空间配置器std_alloc 笔记

C++的内存分配基本操作是 ::operator new(),内存释放是 ::operator delete(),这里两个全局函数相当于C的malloc和free; 
std::alloc 设计了双层配置器,第一层直接用了malloc和free,内存不足时用函数指针模拟C++中set_new_handler方式进行自定义处理函数;第二层:超过128bytes时,便使用第一层配置器,小于128bytes则采用memory pool方式。

1,第一级配置器 __malloc_alloc_template

用C模拟C++中set_new_handler方式处理oom的原因C++并未提供realloc()的内存重分配操作加其他历史原因。

static void *oom_malloc(size_t);
static void *oom_realloc(void *, size_t);
static void (* __malloc_alloc_oom_handler)(); static void (* set_malloc_handler(void (*f)())) () {
void (* old)() = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = f;
return (old);
} // 初始值设为0,留给客户端自行配置(调用set_malloc_handler配置)
void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler) () = ;

第一级的allocate和deallocate其实就是malloc和free的简单封装和oom处理,oom处理即通过上面的设置的处理函数 来 反复调用 以及 malloc或realloc来分配内存知道成功;如果不设置__malloc_alloc_oom_handler,直接__THROW_BAD_ALLOC抛出bad_alloc,直接终止程序。

2,第二级配置器__default_alloc_template

小于128bytes通过第二级配置器分配内存,避免太多小额区块造成内存碎片。 
自由链表数据结构,free_lists管理的大小分别为8,16,24,…,128bytes,共16个链表结点。 
自由链表结点如下,

union obj {
union obj *free_list_link;
char client_data[];
};

union节省了额外内存负担,未使用时视为obj指针,指向相同形式的另一个obj;实际使用时,视为char 指针,指向实际区块。 
__default_alloc_templateROUND_UP比较有技巧性,避免除法的使用,改用位运算上调至8的倍数。

enum { __ALIGN =  };  //小区块的上调边界

// 将bytes上调至8的倍数
// testcase: 比如传入 bytes = 7;(0b1110 & ~7), ~7 = -8, 负数的二进制特殊0b...11111000(高位均为1), &操作的结果=8,完美避免除法的计算。
static size_t ROUND_UP (size_t bytes) {
return (((bytes) + __ALIGN - ) & ~(__ALIGN - ));
}

2.1 allocate函数

  • 1,大于128bytes,直接跳转至第一级分配器;
  • 2,如上图,根据传入的大小找到free_lists中合适的区块;
  • 3,返回值设置为第一个可用指针;
  • 4,my_free_list指向下一个可用区块;

2.2 deallocate(void *p, size_t n)函数

  • 1,大于128bytes,直接跳转至第一级分配器;调用释放函数;
  • 2,临时指针q指向传入的需要释放的p;
  • 3,如上图,根据传入的大小找到free_lists中合适的区块;
  • 4,q->free_list_link指向my_free_list的可用区块;
  • 5,my_free_list再指回q,即q再为可用区块,相当于回收(并未真正释放,只是再次纳入可用,下次直接可用于其他的数据分配)。

2.3 重新填充refill函数

前面的allocate函数失败时,会调用refill重新填充free_lists中传入大小的槽位的空间,通过调用chunk_alloc。如果只取到一个区块,直接返回;如果多个需要一个个串接起来(obj->free_list_link循环指针操作)。详细说明下面的代码注释。

...
void* refill(size_t n) {
...
//取到多个区块后循环指针操作
my_free_list = free_list + FREELIST_INDEX(n); result = (obj *)chunk; //这一块准备返回给客户端,即已经将要被使用,不再被纳入free范畴
*my_free_list = next_obj = (obj *)(chunk + n); //free范畴的区块,从下一块开始
//第一个i = 0已经是将要被客户端使用的区块,从1开始
for (int i = ; ; i++) {
current_obj = next_obj;
next_obj = (obj *)((char *)next_obj + n);
//最后一个指向空指针
if (nobjs - == i) {
current_obj->free_list_link = ;
break;
} else {
//串接各个区块
current_obj->free_list_link = next_obj;
}
}
return result;
}

2.4 内存池取空间给free_list,chunk_alloc(size_t size, int &nobjs) 函数

内存池可用空间通过size_t bytes_left = end_free - start_free; 得来;

  • 1,第一种情况内存池剩余空间满足需求,直接返回start_free,再更新start_free += total_bytes; 的地址,nobjs不变,等于默认值20;
  • 2,第二种情况内存池剩余空间不足以满足全部需求,但能满足一个以上的需求;做除法,能满足几个是几个,其他同情况一,唯一修改nobjs的地方,不足20时,实际分配的数量;
  • 3,第三种情况内存池连一个区块的大小都无法提供; 
    • 首先从内存池中找残余的空间,调整free list,将内存池中残余空间编入;
    • 分配两倍于需求量 + 随配置次数增加的附加量 
      //首先设置将要分配的更多的内存以补充内存池的区块大小
      size_t bytes_to_get = * total_bytes + ROUND_UP(heap_size >> );
    • 如果分配失败,循环检测内存池各大小区块可用的空间,如图中的 #7 (64bytes) 从 32bytes末端取内存池中残余空间;递归调用chunk_alloc,最终会在1或2中终止,nobjs <= 20; 
      如果更严重的情况,到处都没有内存可用,调用第一级配置器,其中有oom处理机制;
    • 如果成功,如图96bytes对应20个区块,剩余的加入内存池,供后续使用。递归调用chunk_alloc,最终会在1或2中终止,nobjs <= 20。

小结

std::alloc 设计了双层配置器,超过128bytes时,便使用第一层配置器,简单封装malloc和free,oom处理函数等;小于128bytes通过第二级配置器分配内存,内存池方式处理小块内存,避免太多小额区块造成内存碎片。

参考

《STL 源码剖析》

STL的空间配置器std_alloc 笔记的更多相关文章

  1. 带你深入理解STL之空间配置器(思维导图+源码)

    前不久把STL细看了一遍,由于看得太"认真",忘了做笔记,归纳和总结这步漏掉了.于是为了加深印象,打算重看一遍,并记录下来里面的一些实现细节.方便以后能较好的复习它. 以前在项目中 ...

  2. STL之空间配置器allocator

    摘要 C++STL的空间配置器将内存的分配.释放,对象的构造.析构都分开执行,内存分配由alloc::allocate()负责,内存的释放由alloc::deallocate()负责:对象的构造由:: ...

  3. STL 之 空间配置器(allocator)

    一.SGI 标准的空间配置器,std::allocator SGI也定义了一个符合部分标准,名为allocator的配置器,但是它自己不使用,也不建议我们使用,主要原因是效率不佳. 它只是把C++的操 ...

  4. STL之空间配置器

    在前面很多随笔里都有提到new对象是先分配内存然后初始化对象,主要是对operator new和placement new的使用 在SGI STL中内存的分配和初始化是分开的,分配内存是使用类模板,模 ...

  5. [stl] SGI STL的空间配置器

    第一级空间配置器 第一级配置以malloc(), free(), realloc()等c函数执行实际的内存配置,释放.重配置操作,并实现出类似c++ new handler的机制.它不能直接使用c++ ...

  6. C++ STL学习之 空间配置器(allocator)

    众所周知,一般情况下,一个程序包括数据结构和相应的算法,而数据结构作为存储数据的组织形式,与内存空间有着密切的联系. 在C++ STL中,空间配置器便是用来实现内存空间(一般是内存,也可以是硬盘等空间 ...

  7. STL——模拟实现空间配置器

    目录 问题 SGI版本空间配置器-std::alloc 一级空间配置器 二级空间配置器 Refill.chunkAlloc函数 最后,配置器封装的simple_alloc接口 问题 我们在日常编写C+ ...

  8. STL源码剖析之空间配置器

    本文大致对STL中的空间配置器进行一个简单的讲解,由于只是一篇博客类型的文章,无法将源码表现到面面俱到,所以真正感兴趣的码农们可以从源码中或者<STL源码剖析>仔细了解一下. 1,为什么S ...

  9. 【陪你系列】5 千字长文+ 30 张图解 | 陪你手撕 STL 空间配置器源码

    大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub https://github.com/rongweihe/MoreT ...

随机推荐

  1. [第一阶段] Python学习

    首先声明一下,我这个学习计划是关于学习Python的. 先说一下起因:我自己接触Python算是很久了,目前仍没学会,很失败,很惭愧.所以这次一方面简单分析一下自学会碰到的问题:另一方便,我想到了一种 ...

  2. MAMP 环境下为 php 添加 pcntl 扩展

    前言: pcntl 介绍 pcntl 扩展可以支持 PHP 的多线程操作.(非Unix类系统不支持此模块) phpize 介绍 phpize 可以用来给 PHP 动态的添加扩展.比如编译 PHP 时忘 ...

  3. Hadoop集群

    你可以用以下三种支持的模式中的一种启动Hadoop集群: 单机模式 伪分布式模式 完全分布式模式 单机模式的操作方法 默认情况下,Hadoop被配置成以非分布式模式运行的一个独立Java进程.这对调试 ...

  4. SpringMVC+Spring 事务无法回滚的问题

    问题描述: Controller里面执行Service的方法,Service方法抛出异常,但是没有按照事务配置的方式回滚: Service的事务配置没有问题: 出现此问题的原因: 在springmvc ...

  5. php学习之目录

    一. 关于php中dirname(_file_)的使用 php中定义了一个很有用的常数,即 __file__ 这个内定常数是当前php程序的就是完整路径(路径+文件名). 即使这个文件被其他文件引用( ...

  6. wifi驱动总结(1)

    一.wifi平台设备驱动注册过程Path:Rtw_android.c (rk3399\kernel\drivers\net\wireless\rockchip_wlan\rtl8723au\os_de ...

  7. 文本主题模型之LDA(二) LDA求解之Gibbs采样算法

    文本主题模型之LDA(一) LDA基础 文本主题模型之LDA(二) LDA求解之Gibbs采样算法 文本主题模型之LDA(三) LDA求解之变分推断EM算法(TODO) 本文是LDA主题模型的第二篇, ...

  8. 【JAVAWEB学习笔记】07_BootStrap、Viewport介绍

    今天主要学习了BootStrap,viewport的介绍和最后对网站进行了重构 今天晨读单词: Compatible:兼容性 viewport:视口 device:设备 initial:初始化(缩写i ...

  9. Jmeter3.0新特性

    2016-5-19昨日,Jmeter又更新了新版本. 那么新版本有哪些新特性呢? Changes   This page details the changes made in the current ...

  10. LINQ之LINQ to Objects(上)

    LINQ概述 LINQ,语言集成查询(Language Integrated Query),它允许使用C#或VB代码以查询数据库相同的方式来操作不同的数据源. 1.LINQ体系结构 从上图可以看出,L ...