本文主要内容如下:
1. STL为什么需要空间配置器
2. STL空间配置器实现的原理
3. STL空间配置器简单的测试
前言
很久以前看过侯捷先生的STL源码分析一书,也大致明白了STL实现的原理,但是对于编程而言,如果自己不去实现代码,调试代码,光看书,其实很难深刻的理解STL源码的逻辑(光是看书顶多也就是了解的水平,当然这是对于一般的程序员而言)。前段时间决定实现一下STL相关的容器(用到了C++11新特性),从而更加深入的理解STL,并提升自己的C++ 模板编程的功底。
先给出一张测试空间配置的运行截图(CLion + cmake):
首先本文给出的是类alloc的实现
class alloc{
//............
}
下篇blog给出allocator的实现(allocator主要是把上面的alloc作为静态的成员,做了一些简单的封装),并给出使用配置器allocator完成一个基本的vector(SimpleVec).
template<class T>
class allocator{
//..................
}
template <class T, class Alloc = allocator<T>>
class SimpleVec{
//.....
}
1. STL为什么需要空间配置器
程序中我们经常动态申请,释放内存,这会带来如下两个问题:
问题1:就出现了内存碎片问题。(包括外碎片问题)
问题2:一直在因为小块内存而进行内存申请,调用malloc,系统调用产生性能问题。
内碎片:因为内存对齐/访问效率(CPU取址次数)而产生 如 用户需要3字节,实际得到4或者8字节的问题,其中的碎片是浪费掉的。
外碎片:系统中内存总量足够,但是不连续,所以无法分配给用户使用而产生的浪费。下边简单图解
2. STL空间配置器实现的原理
这两个问题解释清楚之后,就来谈STL空间配置器的实现细节了实现策略
if 用户申请空间 > 128:
调用一级空间配置器
else:
调用二级空间配置器
大致实现为:
二级空间配置器由内存池以及伙伴系统:自由链表组成
一级空间配置器直接封装malloc,free进行处理,增加了C++中的set_handler机制(这里其实也就是个略显牵强的装饰/适配模式了),增加内存分配时客户端可选处理机制。
二级空间配置器:
成员以及接口定义如下:
class alloc{
private:
enum EAlign{ ALIGN = 8};//小型区块的上调边界
enum EMaxBytes{ MAXBYTES = 128};//小型区块的上限,超过的区块由malloc分配
//free-lists的个数
enum ENFreeLists{ NFREELISTS = (EMaxBytes::MAXBYTES / EAlign::ALIGN)};
enum ENObjs{ NOBJS = 20};//每次增加的节点数
private:
//free-lists的节点构造
union obj{
union obj *next;
char client[1];
};
static obj *free_list[ENFreeLists::NFREELISTS];
private:
static char *start_free;//内存池起始位置
static char *end_free;//内存池结束位置
static size_t heap_size;//
private:
//将bytes上调至8的倍数,例如 (1, 3, 7) -> 8, (9, 13) -> 16
static size_t ROUND_UP(size_t bytes){
return ((bytes + EAlign::ALIGN - 1) & ~(EAlign::ALIGN -