前言

在STL中,容器的定义中都带一个模板参数,如vector

template <class T, class Alloc = alloc>
class vector {...}

其中第二个参数就是该容器使用的空间配置器,其中缺省使用STL已经实现的空间配置器(alloc),

该配置器使用malloc/free等为vector分配内存。

缺省的空间配置器

alloc定义了两级的空间配置器,第一级是对malloc/free简单的封装。

而为了解决内存碎片的问题,跟进行内存管理,alloc实现的第二级的空间配置器。

第二级空间配置器在分配大块内存(大于128bytes)时,会直接调用第一级空间配置器,

而分配小于128bytes的内存时,则使用内存池跟free_list进行内存分配/管理。

第一级空间配置器

基本实现如下(跟SGI STL可能有点出入,主要是提取核心的内容)

 class base_alloc {
public:
// 只是对malloc/free的简单封装
static void* allocate(size_t n)
{
void* res = malloc(n);
if ( == res) res = oom_malloc(n);
return res;
}
static void* reallocate(void* p, size_t new_sz)
{
void* res = realloc(p, new_sz);
if ( == res) res = oom_realloc(p, new_sz);
return res;
}
static void deallocate(void* p)
{
free(p);
}
// 用来设置内存不足时的处理函数 该函数参数跟返回值都是一个函数指针
// 一般会抛出异常/尝试回收内存
static void(*set_handler(void(*f)()))()
{
void(*old)() = _oom_handler;
_oom_handler = f;
return old;
}
private:
// 用来处理内存不足的情况
static void* oom_malloc(size_t n)
{
void(*my_handler)();
void* res; for (;;)
{
my_handler = _oom_handler;
if ( == my_handler) { return NULL; }
(*my_handler)();
if (res = malloc(n)) return res;
}
}
// 用来处理内存不足的情况
static void* oom_realloc(void* p, size_t n)
{
void(*my_handler)();
void* res; for (;;)
{
my_handler = _oom_handler;
if ( == my_handler) { return NULL; }
(*my_handler)();
if (res = reallocate(p, n)) return res;
}
}
// 由用户设置,在内存不足的时候进行处理,由上面两个函数调用
static void(*_oom_handler)();
}; // 处理函数默认为0
void(*base_alloc::_oom_handler)() = ;

它可以设定一个处理内存不足的时候的处理函数(跟set_new_handler类似)。

第二级空间配置器

该配置器维护一个free_list,这是一个指针数组。

在分配内存的时候,补足8bytes的倍数,free_list数组中每个指针分别管理分配大小为8、16、24、32...bytes的内存。

下图表示从二级空间配置器中分配内存时是如何维护free_list的(建议参考下面源码阅读)。

开始所有指针都为0,没有可分配的区块时(就是free_list[i]==0)会从内存池中分配内存(默认分配20个区块)插入到free_list[i]中。

然后改变free_list[i]的指向,指向下一个区块(free_list_link指向下一个区块,如果没有则为0)。

下图表示二级空间配置器回收内存时是如何维护free_list结构的。

回收的时候只是将区块插入到free_list[i]的开头,这块内存用于下次分配的时候使用。

该配置器实现如下(同样提取核心的部分):

 enum { _ALIGN =  };    // 对齐
enum { _MAX_BYTES = }; // 区块大小上限
enum { _NFREELISTS = _MAX_BYTES / _ALIGN }; // free-list个数 class default_alloc {
private:
// 将bytes上调到8的倍数
static size_t ROUND_UP(size_t bytes)
{
return (bytes + _ALIGN - ) & ~(_ALIGN - );
}
private:
union obj {
union obj* free_list_link;
char client_data[];
};
private:
// 16个free-lists 各自管理分别为8,16,24...的小额区块
static obj* free_list[_NFREELISTS];
// 根据区块大小,决定使用第n号free-list
static size_t FREELIST_INDEX(size_t bytes)
{
return (bytes + _ALIGN - ) / _ALIGN - ;
}
// 分配内存,返回一个大小为n的区块,可能将大小为n的其他区块加入到free_list
static void* refill(size_t n)
{
// 默认分配20个区块
int nobjs = ;
char* chunk = chunk_alloc(n, nobjs);
obj** my_free_list;
obj* result, *current_obj, *next_obj; // 如果只分配了一个区块,直接返回
if ( == nobjs) return chunk;
// 否则将其他区块插入到free list
my_free_list = free_list + FREELIST_INDEX(n);
result = (obj*)chunk;
// 第一个区块返回 后面的区块插入到free list
*my_free_list = next_obj = (obj*)(chunk + n);
for (int i = ;; ++i)
{
current_obj = next_obj;
next_obj = (obj*)((char*)next_obj + n);
// 最后一个next的free_list_link为0
if (nobjs - == i)
{
current_obj->free_list_link = ;
break;
}
current_obj->free_list_link = next_obj;
}
return result;
}
// 分配内存
// 在内存池容量足够时,只调整start_free跟end_free指针
// 在内存池容量不足时,调用malloc分配内存(2 * size * nobjs + ROUND_UP(heap_size >> 4),每次调整heap_size += 本次分配内存的大小)
static char* chunk_alloc(size_t size, int& nobjs); static char* start_free; //内存池的起始位置
static char* end_free; //内存池的结束位置
static size_t heap_size; //分配内存时的附加量
public:
static void* allocate(size_t n)
{
obj** my_free_list;
obj* result;
// 大于128就调用第一级空间配置器
if (n > (size_t)_MAX_BYTES)
return base_alloc::allocate(n); // 寻找适当的free-list
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
// 没有可用的free list
if (result == )
return refill(ROUND_UP(n)); // 调整free list 移除free list
*my_free_list = result->free_list_link;
return result;
}
static void deallocate(void* p, size_t n)
{
obj* q = (obj*)p;
obj** my_free_list; // 大于128就调用第一级空间配置器
if (n > (size_t)_MAX_BYTES)
{
base_alloc::deallocate(p);
return;
} // 寻找对应的free list
my_free_list = free_list + FREELIST_INDEX(n);
// 调整free list 回收区块(将区块插入到my_free_list)
q->free_list_link = *my_free_list;
*my_free_list = q;
}
static void reallocate(void* p, size_t old_sz, size_t new_sz);
}; char* default_alloc::start_free = ;
char* default_alloc::end_free = ;
size_t default_alloc::heap_size = ;
default_alloc::obj* default_alloc::free_list[_NFREELISTS] = { , , , , , , , , , , , , , , , };

STL源码剖析(空间配置器)的更多相关文章

  1. STL源码剖析 — 空间配置器(allocator)

    前言 以STL的实现角度而言,第一个需要介绍的就是空间配置器,因为整个STL的操作对象都存放在容器之中. 你完全可以实现一个直接向硬件存取空间的allocator. 下面介绍的是SGI STL提供的配 ...

  2. STL源码剖析——空间配置器Allocator#2 一/二级空间配置器

    上节学习了内存配置后的对象构造行为和内存释放前的对象析构行为,在这一节来学习内存的配置与释放. C++的内存配置基本操作是::operator new(),而释放基本操作是::operator del ...

  3. STL源码剖析——空间配置器Allocator#1 构造与析构

    以STL的运用角度而言,空间配置器是最不需要介绍的东西,因为它扮演的是幕后的角色,隐藏在一切容器的背后默默工作.但以STL的实现角度而言,最应该首先介绍的就是空间配置器,因为这是这是容器展开一切运作的 ...

  4. STL源码剖析——空间配置器Allocator#3 自由链表与内存池

    上节在学习第二级配置器时了解了第二级配置器通过内存池与自由链表来处理小区块内存的申请.但只是对其概念进行点到为止的认识,并未深入探究.这节就来学习一下自由链表的填充和内存池的内存分配机制. refil ...

  5. STL源码剖析:配置器

    作用:对内存的管理 接口:申请和释放 内容: 几个全局函数 一级配置器 二级配置器 准备知识 POD是什么: Plain Old Data简称POD,表示传统的C语言类型:与POD类型对应的是非POD ...

  6. 《STL源码剖析》相关面试题总结

    原文链接:http://www.cnblogs.com/raichen/p/5817158.html 一.STL简介 STL提供六大组件,彼此可以组合套用: 容器容器就是各种数据结构,我就不多说,看看 ...

  7. 面试题总结(三)、《STL源码剖析》相关面试题总结

    声明:本文主要探讨与STL实现相关的面试题,主要参考侯捷的<STL源码剖析>,每一个知识点讨论力求简洁,便于记忆,但讨论深度有限,如要深入研究可点击参考链接,希望对正在找工作的同学有点帮助 ...

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

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

  9. 《STL源码剖析》环境配置

    首先,去侯捷网站下载相关文档:http://jjhou.boolan.com/jjwbooks-tass.htm. 这本书采用的是Cygnus C++ 2.91 for windows.下载地址:ht ...

随机推荐

  1. Python 进阶 之 socket模块

    Python Socket 编程详细介绍 转载地址:https://gist.github.com/kevinkindom/108ffd675cb9253f8f71?signup=true Pytho ...

  2. activemq修改admin密码

    vim /usr/local/activemq/conf/jetty-realm.properties 用户名:密码,角色 mq: 123, admin 那么打开管理后台,用户是mq,密码是123,具 ...

  3. POJ1251 Jungle Roads(Kruskal)(并查集)

    Jungle Roads Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 23882   Accepted: 11193 De ...

  4. 【kd-tree】bzoj3489 A simple rmq problem

    Orz zyf教给蒟蒻做法 蒟蒻并不会这题正解……(可持久化树套树?...Orz 对于每个点,我们可以求出pre[i],nex[i],那么询问的答案就是:求max (a[i]),其中 i 满足(pre ...

  5. 【分块】bzoj3226 [Sdoi2008]校门外的区间

    题解见 : http://blog.csdn.net/iamzky/article/details/41088151 ORZ ZKY 2个懒标记:是否翻转,覆盖成了什么. 怎么处理一个块上有两个标记的 ...

  6. Target runtime Apache Tomcat 7.1 is not defined 解决方法

    在工作台目录下找到 自己操作的项目的文件夹 /.settings/org.eclipse.wst.common.project.facet.core.xml 打开后,显示 <?xml versi ...

  7. FrameLayout 布局

    (一) 1.效果图:颜色一直在改变,实现霓虹灯的效果 2.activity_main.xml <?xml version="1.0" encoding="utf-8 ...

  8. Redis Exception: Exceeded timeout of 00:00:03

    Redis Exception: Exceeded timeout of 00:00:03 居然是 重启了网管, 把网络禁用重启就好了. 服 最终更新: 原来是架构湿      设置为每分钟只能读取6 ...

  9. JVM类加载的那些事

    原文出处: 占小狼 前言 Java源代码被编译成class字节码,最终需要加载到虚拟机中才能运行.整个生命周期包括:加载.验证.准备.解析.初始化.使用和卸载7个阶段.   加载 1.通过一个类的全限 ...

  10. C#之Hello World(入门 )

    C#是一种简单.现代.面向对象和类型安全的编程语言. C#由C和C++发展而来.C#(英文发音C sharp)牢固地植根于C和C++语言族谱中,是Microsoft专门为使用.NET平台而创建的. • ...