空间配置器 & 内存池 设计 & set_new_handler: STL 2.2

从 STL 实现角度 看, 
`allocator 是 STL 其他组件实现 的 基础`

allocator 称为 空间配置器, 而不是 内存配置器, 
因为 空间 可以是 内存/磁盘/其他存储介质

1 仿 SGI std::allocator 设计 1个 符合 STL规范 的 allocator

// MyAllocator.h
#ifndef _MYALLOC_
#define _MYALLOC_

#include <new>     // placement new, set_new_handler()
#include <cstddef> // ptrdiff_t size_t
#include <cstdlib> // exit()
#include <climits> // UINT_MAX
#include <iostream>// ceer

namespace MY
{

//(1) 4大 global func: 分配/释放memory 构造/析构 obj
// 用于 被 allocator class 相应 mem func 调用
template <class T>
T* _allocate(ptrdiff_t n, T*)
{
    //set_new_handler(0);

    T* p = (T*)(::operator new((size_t)(n * sizeof(T) ) ) );
    if (p == 0)
    {
        std::cout << "out of memory" << std::endl;
        exit(1);
    }
    return p;
}

template <class T>
void _deallocate(T* p)
{
    ::operator delete(p);
}

template <class T1, class T2>
void _construct(T1* p, const T2& value)
{
    new(p) T1(value); // placement new
}

template <class T>
void _destory(T* p)
{
    p->~T();
}

template <class T>
class allocator
{
public:
    typedef size_t      size_type;
    typedef ptrdiff_t   difference_type;
    typedef T           value_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;

    template <typename U>
    struct rebind
    {
        typedef allocator<U> other;
    };

    // 4大函数: 分配/释放内存 构造/析构对象
    pointer allocate(size_type n)
    {
        return _allocate( (difference_type)n, (pointer)0);
    }

    void deallocate(pointer p, size_type n) // n 没用
    {
        _deallocate(p);
    }

    void construct(pointer p, const T& value)
    {
        _construct(p, value);
    }
    void destory(pointer p)
    {
        _destory(p);
    }

    pointer address(reference x)
    {
        return (pointer)&x;
    }
    const_pointer const_address(const_reference x)
    {
        return (const_pointer)&x;
    }
    size_type max_size() const
    {
        return size_type(UINT_MAX / sizeof(T));
    }
};

} // end of namespace MY

#endif //MYAllocator
#include "MyAllocator.h"
#include <vector>
#include <iostream>
int main()
{
    int a[3] = {0, 1, 2};
    std::vector<int, MY::allocator<int> > vec(a, a+3);

    for(int i = 0; i < vec.size(); i++)
        std::cout << vec[i] << std::endl;
}
Ubuntu 下 g++ 编译 运行正常, gdb调试
image.png

2 SGI 版本 从 标准 std::allocator 到 具有内存池设计 的 std::alloc

1.  P.J. 分配器 和 gcc2.91 标准分配器, 都叫 std::allocator, 
它 没有 独特设计, 底层都是 直接调 malloc / free
malloc 分配的内存 = 申请的内存 + 上/下 cookie
若频繁申请 小型区块, 比如 8Bytes, 
则 cookie 就占了 一半内存
-> `空间利用率低, 且易造成 内存碎片 
-> 解决: 内存池 设计
2. gcc2.91 有 标准分配器 std::allocator 却不用, 
而是用 special allocator std::alloc

其设计思维为:

(1) 多线程: 本节不讨论
(2) 模拟 C++ set_new_handler, 以处理 内存不足 状况

(3) 内存池 设计: 以应对 频繁申请 小型区块 时, 空间利用率低 与 内存碎片 问题

两级 配置器:

第1级 处理 >128 Bytes 区块: 仿 set_new_handler

第2级 处理 8*n (n = 1,...,16) <= 128 Bytes 区块: 
    16 个 free-list + 内存池

3. 16 个 free-list + 内存池

申请 n <= 128 Bytes 时, 多分配若干 Bytes, 存于

两级缓存

第 1 / 2 级放 free-list / 内存池, 使得 

下次申请 ( 的 区块大小 前面已申请过 ) 时, 可能 直接/间接 从 free-list / 内存池 中 取, 而不是 又 让 OS 分配

// request memory block 的 process strategy:
if (memory pool 完全满足需求: <= 20 个 区块. 
        若 modify internal_req_obj_num, 则 取 < )
    (1) 提供 所需
else if (memory pool 不完全满足需求, 但能提供 n >=1 个 区块)
    (2) 提供 n 个区块
        modify internal_req_obj_num
else // memory pool 连 1个 区块 也 提供不了                 
    (3) 先将 memory pool 所有剩余 space 配给 相应 free_list[i]
    (4) 再向 OS 申请 -> malloc 分配 heap memory: bytes_to_get
    若 heap memory 不足 -> malloc 失败 
        (5) 循环遍历 free_list[i, i+1, ...] 中 
        larger( => 必为 req_mem_blk_size_8_align 整数倍 
                => 必 在 更后面的 free_list[j>i] )
        且 unused 区块 
        若有 
            (6) 拨出 1 个 mem_blk 放 memory pool(当前已 empty)
                
            (7) 递归 调 chunk_alloc 自身, 再次 从 memory pool 申请
                modify internal_req_obj_num             
4. free-list + 内存池 分配 的 例子

(1) 初次申请 32 Bytes
free-list[32/8 -1= 3] 和 内存池 均 empty,  

-> malloc 分配 2 * 20 * 32 Bytes + 
    随 分配次数增大而增大的 附加量 ( 初始 为 0 ) 
= 40 * 32 Bytes:
1) 第   1 个 32 Bytes return 给 client
2) 随后 19 * 32 Bytes 放 free-list ( 第 1 级 缓存 )
3) 随后 20 * 32 Bytes 放 内存池 ( 第 2 级 缓存 )

(2) 第 2 次 申请 64 Bytes
free-list[64/8 - 1] = free-list[7] empty
-> 尝试 从 内存池 取, 取 20*32/64 = 10 个 64 Bytes 区块:
    第 1 个 return 给 client /
    随后 (只剩) 9 个放 free-list[7] /
    内存池 become empty /

(3) 第 3 次 申请 96 Bytes
free-list[11] 和 内存池 均 empty
-> malloc 分配 2 * 20 * 96 + n (余量) Bytes:
    第   1 个 96 Bytes return 给 client /
    随后 19 * 96 Bytes 放 free-list[11] /
    其余放 内存池
image.png

5. 1/2 级 allocator + wrapper class template simple_alloc + vector 容器 -> framework

下图中 第1级 配置器 type 别名 不对, 应该为 malloc_alloc
image.png
两级 allocator 本身 均为 class template: 
    但无 template para, 都 只是 普通参数
    inst 完全没用 + 第 2 级 allocator 多线程参数 本节不考虑 
-> 本文 source code 中 allocator name 中不带 template

// 第 1 级 allocator
template <int inst>
class __malloc_alloc_template;

// 第 2 级 allocator
template <bool threads, int inst>
class __default_alloc_template;
别名:
__malloc_alloc / malloc_alloc

__default_alloc_template / alloc

simple_alloc<...> / data_allocator
#include <cstddef> // std::size_t

//------ 1. 第 1 级 allocator
class __malloc_alloc
{
public:
    static void* allocate(size_t n)
    {
        //(1)
        set_new_handler(handler); //<=> set_new_handler(&handler);

        void* result = malloc(n);

        // malloc 失败时, 调 oom_malloc()
        if (0 == result)
            result = oom_malloc(n);
        return result;
    }
        
    // ...
    
    static void deallocate(void* p, size_t)
    {
        free(p);
    }   
};
typedef __malloc_alloc malloc_alloc;


//------ 2. 第 2 级 allocator
class __default_alloc
{
public
    static void* 
    allocate(size_t req_blk_size)
    {   // client 请求: req_blk_size
        if( req_blk_size > (size_t)MAX_BYTE )
        {
            return ( malloc_alloc::allocate(req_blk_size) );
        }
        
        // ...
        
        void *result_prev = refill( ROUND_UP(req_blk_size) );
    }
    
    void* refill(size_t req_blk_size_align)
    {
        //(1) req_blk_num: allocator 内部 (欲) 请求
        // 1) 尝试 向 memory pool 请求 20 个 memory block 
        //   放 相应 free_list:
        // 2) pass by reference to chunk_alloc to be modified
        int req_blk_num = 20; 
        
        //(2) request memory_block from memory pool
        // 1) memory pool 能提供 几(>= 1) 个 memory block,
        //    就 分配 几个 
        // 2) 若 连 1 个 也 提供不了, 
        //    则 剩余 space 先配给 相应 free_list[i]
        //       再 向 OS 申请: malloc 分配   
        char* chunk = 
            chunk_alloc(req_blk_size_align, req_obj_num);
            
        //(3) 将 分配的 chunk memory:
        // 1) return to client 
        // 2) 放 相应 free_list[i]
        // 3) 放 memory pool (maybe)
            
    }
    
    // request memory_block from memory pool
    // memory_block 用 union obj 表示
    char* chunk_alloc(size_t req_blk_size_align, int& req_obj_num)
    {
        if ( memory pool 完全满足需求: <= 20 个 区块. 若 modify req_obj_num, 则 取 < )
            提供 所需
        else if (memory pool 不完全满足需求, 但能提供 n >=1 个 区块)
            提供 n 个区块
                modify req_obj_num
        else // memory pool 连 1个 区块 也 提供不了
        {    
            bytes_to_get = 2 * half_blks_bytes_to_get  
                           + ROUND_UP(heap_size >> 4); 
                           
            (4) 先将 memory pool 所有剩余 space 配给 相应 free_list[i]
            
            
            (5) 再向 OS 申请 -> malloc 分配 heap memory: bytes_to_get
            若 heap memory 不足 -> malloc 失败
            {   
                (6) 从 req_blk_size_align 相应 free_list[i] 
                    下一 free_list 开始,
                循环遍历 后面 free_list[ j > i ]: 可能含 `unused larger 区块`
                {   
                    若 free_list[j] 有 `unused larger 区块(必为 req_blk_size_align 整数倍)`
                    {
                        (7) 从 free_list[j] 拨出 1 个 mem_blk 
                        放 memory pool(当前已 empty),
                        即 adjust 3 个 pointer:
                            相应 *my_free_list 指向 原先 list 下一 obj
                            memory pool start/end pinter 指向 拨出的 mem_blk 首/尾
                        
                        (8) return ( chunk_alloc(req_blk_size_align, req_obj_num) );
                        递归 调 chunk_alloc 自身, 再次 从 memory pool 申请,
                        以 modify req_obj_num
                            只 递归 1次 就结束 
                            因为 从 相应 free_list[i]
                            拨到 memory pool 的 mem_blk 
                            必然 是 req_blk_size_align 的 整数倍
                    }
                }
                (9) 若执行到 该行 => free_list 也没 空间了
                 1) memory pool 尾 pointer 先置 空: end_free = 0;
                 2) 调 第 1 级 allocator, 看 out-of-memory 机制 是否能 分配
                star_free = (char*)malloc_alloc::allocate(bytes_to_get);
            }
            (8) update heap_size: 
            每次 向 memory pool 请求 memory 时, 
            若 走 case3 向 OS 请求,
            则 要 增加 heap_size +=  byte_to_get
            使得 下次 再向 OS 申请 时, 再多申请些
                
            (10) return ( chunk_alloc(req_blk_size_align, req_obj_num) );
            递归 调 chunk_alloc 自身, 再次 从 memory pool 申请,
        }
    }
};

// default value of template para in vector
typedef __default_alloc alloc; 


//------ 3. wrapper class template: simple_alloc
//(1) 意义: 两级 allocator 分配/释放 memory 的 
//       interface func para 是 req_blk_size_align
//    => directly call 时, 要 指定 req_blk_size_align
//       而 对 client 而言, 更方便的 interface 是
//       只指定 要 分配/释放 的 elem_type 及 elem_num
//    -> solution: 加 1 层 中介层
//       1) 据 elem_type 及 elem_num -> 求 req_blk_size_align 
//       2) call allocate/deallocate
//    -> implememt: wrapper class template

//(2) 分配/释放 memory 的 strategy:
// directly call 第 2 级 allocator 的 allocate/deallocate func
// -> 据 memory size > 128 Bytes 时, 
//    再 转调 第 1 级 allocator 的 allocate/deallocate func
template<class T, class Alloc>
class simple_alloc
{
public:
    static T* 
    allocate(size_t elem_num)
    {
        return 0 == elem_num ? 0 : 
            T * Alloc::allocate(elem_num * sizeof(T) );
    }

    static void 
    deallocate(T* p, size_t elem_num)
    {
        if( elem_num != 0)
            Alloc::deallocate(p, elem_num * sizeof(T) );
    }
};

//------ 4. 容器: 直接 用 wrapper class
// 容器 的 2 个 template para 绑定 在一起
template <class T, class Alloc = alloc >
class vector
{
    // ...
protected:
    typedef simple_alloc<T, Alloc> data_allocator;
    
    iterator // T*
    allocate_and_fill(size_type elem_num, const T& x)
    {
        iterator result = data_allocator::allocate(elem_num);
        
        std::uninitialized_fill_n(result, elem_num, x);
        
        return result;
    }
    
    void deallocate()
    {
        if(start)
            data_allocator::deallocate(start, 
                            end_of_storage - start);
    }
};
//------ heap_size
请求次序 req_blk_size      req_obj_num  是否向 OS 请求
1          32                20                  是 
2          64                20->10              否 
3          96                20->                是 
                                                
请求次序  bytes_to_get                heap_size
1         2*(20*32)=1280+0            0 -> += 1280 = 1280
2         不计算                     不计算
3         2*(20*96)+1280>>4 -> 8倍数  1280 -> += 3920 = 5200
          = 3840 + 80 = 3920                        
                    
(1) client 请求的 memory block: 
    req_blk_size

(2) allocator 内部 欲请求 byte 数: 分 2 部分
1) 多请求 的 若干块: internal_req_obj_num

2) 每次向 OS 请求, 会 将 
allocator 内部 欲请求 byte 数
累加 到 1个 static 累加量 heap_size 中,
下次 再 OS 请求 时, 在 `多请求的 若干块 之外`,
再 附加 该 累加量 heap_size 除 16 -> 上调到 8 的 倍数 
使得 再多分配些 

allocator 内部 欲请求 byte 数
= bytes_to_get 
= 2 * half_blks_bytes_to_get  + ROUND_UP(heap_size >> 4)
maybe 不等于 最终分配的 byte 数

half_blks_bytes_to_get 
= req_blk_num_unmodified * req_blk_size_align 
= 20 * req_blk_size_align = 8 的 倍数
          
ROUND_UP(heap_size >> 4) 
= 除 16 result -> 上调到 8 的 倍数 

3 第 1 级 allocator 设计

3.1 std::set_new_handler: <new>

typedef void (* new_handler)();

std::new_handler 
set_new_handler (std::new_handler new_p) throw();

std::set_new_handler is called by the default allocation functions ( operator new and operator new[] ) when they fail to allocate storage

Para:
new_p: pointer to function of type std::new_handler, or null pointer

Return value:
previously-installed new handler, or a null pointer value if none was installed.
用途:
1) make more memory available
2) terminate the program (e.g. by calling std::terminate)
3) throw exception of type std::bad_alloc or ...
#include <iostream>
#include <new>

void handler()
{
    std::cout << "Memory allocation failed, terminating\n";
    std::set_new_handler(nullptr);
}

int main()
{
    std::set_new_handler(handler);

    try
    {
        while (true) 
        {
            new int[100000000ul];
        }
    }
    catch (const std::bad_alloc& e)
    {
        std::cout << e.what() << '\n';
    }
}

3.2 第 1 级 allocator: malloc_alloc

模拟 C++ 中 std::set_new_handler, 以处理 memory 不足 的 case

// 模拟 std::set_new_handler implement
typedef void (* new_handler)();

// 2 者 等价: 用 typedef 的 别名 最 simple
new_handler set_new_handler(new_handler pf) 
void (* set_new_handler( void (* pf)() ) ) ()
typedef 优势在此十分明显, 清楚的指出 
set_new_handler return value 为 function pointer type,
the pointed func taking no arguments and returning no value

4 第 2 级 allocator: std::alloc 设计

image.png

5 第1/2 级 allocator source code

1. std::alloc 管理 free_list 的 设计思路

(1) list node 的 pointer 域 不独占 memory 的 设计

通常, list 的 每个 node 要设一 额外 pointer 域 next, 
以 指向 the next node
节省 该 pointer 域 memory space 的 方法:

1) 第 i 条 free_list[ i = 0-15 ] 管理 最多 20 个  node
`每个 node  表示 1个 大小为 8*(i+1) Bytes 的 memory block` 

2) `pointer 域 只占 4 Bytes (32 位 OS)`

3) memory block is `uninitialized` 

设 node 最前面的 4 Bytes memory 的 data structure 为 1 个 名为 obj 的 union

union obj
{
    union obj* next; 
    char client_data[1];       
};

从 第 1 / 2 字段 观之, obj is a pointer to the next obj union / the first char memory of the current memory block

the requested req_blk_num 个 mem_blk 是 连续 memory

用 union 实现 一物二用

使得 管理 list 时, 无需 为 每个 node 单独分配 pointer 域,
提高了 memory 空间利用率

Java 中 无法实现 该 技巧

(2) free_list[ FREE_LIST_NUM ] array

1) 放 private: 只 std::alloc 内部用 

2) array elem: free_list[i = 0-15] 
is the head pointer to the first obj of the ith list
type: obj* 

3) array name: free_list
type: obj**

2. volatile

(1) volatile 
tell compiler 其 修饰的 variable 随时可变,
编译后的 code 每次 read/write 该 variable 时,
`直接 对 variable memory read/write`

若无 volatile, 则 compiler 可能 
借助 register/高速cache 优化 read/store(write)

(2) 含义:
1) volatile `总是 与 compiler 优化 相关`

2) volatile 字面含义是 易变的

(3) 作用:
1) 告诉 compiler 不要 cache variable 到 register/高速cache 

`多任务 / 中断` 下, variable 可能被 other program(如 thread) 改变, 
compiler 无法感知到, volatile 就是 告诉 compiler 
不要 在 两个操作 之间 缓存 variable 到 register/高速cache

2) 对 volatile variable 的 read/write 不会被 优化掉

(4) note: volatile 并不保证 对 内存操作 的 原子性
#include <iostream>

//------ 1. 第 1 级 allocator
class __malloc_alloc
{
private:
    // note: func name 本身就是 func address:
    //  即 func <=> &func

    //(1) new_handler :    为 func pointer type 
    // new_handler pf : pf 为 func pointer
    // void (* pf)()  : pf 为 func pointer
    typedef void (*new_handler)();

    //(2) global handler : record the current func pointer
    static new_handler global_handler;

public:
    static void* allocate(size_t n)
    {
        //(3)
        set_new_handler(handler); //<=> set_new_handler(&handler);

        void* chunk = malloc(n);

        // malloc 失败时, 调 oom_malloc()
        if (0 == chunk)
            chunk = oom_malloc(n);
        return chunk;
    }

    //(4) set_new_handler: 仿 std::set_new_handler
    static new_handler set_new_handler(new_handler pf)
    {
        new_handler previous_handler = global_handler;

        // update global handler to be specified func's pointer
        global_handler = pf;

        return previous_handler;
    }

    //(5) handler func
    static void handler()
    {       // 企图 释放 memory
        std::cout << "make more memory available\n";
        std::set_new_handler(nullptr);
    }

    //(6)
    static void* oom_malloc(size_t n)
    {
        new_handler local_handler = 0;
        void* mem;

        // 不断尝试(这里限制次数) 释放, 配置, 释放, 配置
        for (int i = 0; i < 3; i++)
        {
            // 1) 取 the recorded handler func pointer from global_handler
            local_handler = global_handler;

            if (0 == local_handler)
                continue;

            // 2) 调 specified handler func
            (*local_handler)();

            // 3) malloc again
            mem = malloc(n);
            if (mem)
                return mem;
        }
    }

    static void deallocate(void* p, size_t)
    {
        free(p);
    }
};
typedef __malloc_alloc malloc_alloc;
malloc_alloc::new_handler malloc_alloc::global_handler = 0;


//------ 2. 第 2 级 allocator
//--- 3个 enum
enum
{
    ALIGN = 8 // 最小 memory block
};
enum { MAX_BYTES = 128 };
enum { FREE_LIST_NUM = MAX_BYTES / ALIGN }; // 16 条 free_list 

class __default_alloc
{
private:
    // union 一物二用: 实现 节省 list node 的 pointer 域 空间
    union obj 
    {
        union obj* next;
        char client_data[1];      
    };

    // --- 4 static mem
    // free_list[16] array: 16 free-list
    static obj* volatile 
        free_list[FREE_LIST_NUM];

    // memory pool management: 2 pointer 
    static char* start_free;
    static char* end_free;
    
    // accumulate var: request memory from OS 时, 
    // 用于 下次再 request from OS 时, 附加 request 的量
    static size_t heap_size;


    // request_mem_blk_bytes -> which free_list: free_list[i]
    static size_t freelist_index(size_t request_mem_blk_bytes)
    {
        return (request_mem_blk_bytes + ALIGN - 1) / ALIGN - 1;
    }

    // request_mem_blk_bytes 上调至 8(= b1000) 的倍数:
    // & 1111 1000 = & ~(ALIGN - 1)
    static size_t ROUND_UP(size_t request_mem_blk_bytes)
    {
        return (request_mem_blk_bytes + ALIGN - 1) & ~(ALIGN - 1); 
    }

    static void* refill(size_t n);
    static char* chunk_alloc(size_t n, int& req_blk_num );

public: // 2 interface func
    static void* allocate(size_t req_blk_size);
    static void deallocate(void* p, size_t n);
};

typedef __default_alloc alloc;


//--- 4 static mem data initialize
alloc::obj* volatile 
alloc::free_list[FREE_LIST_NUM] = { 0 };

char* alloc::start_free = 0;
char* alloc::end_free = 0;
size_t alloc::heap_size = 0;

void* alloc::allocate(size_t req_blk_size)
{   // client 请求: req_blk_size, 只 请求 1 个 memory blk
    obj* volatile* my_free_list;
    
    obj* target_free_list_pobj;
 
    if( req_blk_size > (size_t)MAX_BYTES )
    {
        // 转向 调 第 1 级 allocator
        return ( malloc_alloc::allocate(req_blk_size) );
    }

    my_free_list = free_list + freelist_index(req_blk_size);
    target_free_list_pobj = *my_free_list;
    
    if (target_free_list_pobj == 0) // list empty
    {
        // request mem-blk from memory pool
        void *res = refill( ROUND_UP(req_blk_size) );
        return res;
    }

    // seperate the first mem_blk from target_free_list
    *my_free_list = target_free_list_pobj->next;
    
    // void* p = pobj => pobj implicitly converted to void*
    return target_free_list_pobj;
}

void* __default_alloc::refill(size_t req_blk_size_align)
{
    int req_blk_num  = 20;

    //(1) allocator 内部 尝试 / 实际 request 
    // req_blk_num < / = 20 个 区块 from memory pool
    // req_blk_num pass by reference to be modified
    char* chunk = chunk_alloc(req_blk_size_align, req_blk_num );

    obj* volatile* my_free_list;
    obj* return_pobj, * current_pobj, * next_pobj;
    
    //(2) 只请求到 1 个 mem_blk
    if (0 == req_blk_num || 1 == req_blk_num  ) // else >= 2
        return chunk;
    
    //(3) the first mem_blk to be returned to client
    return_pobj = (obj*)chunk; 

    //(4) other req_blk_num -1 个 mem_blks linked to free_list
    my_free_list = free_list 
                 + freelist_index(req_blk_size_align);

    *my_free_list 
        = next_pobj 
        = (obj*)(chunk + req_blk_size_align); 
    
    // 第 2th ~ req_blk_num th mem_blk linked to list
    // the requested req_blk_num 个 mem_blk 是 连续 memory
    // loop every mem_blk: 
    // 1) initialize next_head point to the start 
    // 2) current_head point to next_head
    // 3) next_head updated to point to the real next blk
    //    => [current_head, next_head) 为 the current blk
    // 4) link
    for (int i = 0; ; i++)
    {
        current_pobj = next_pobj;                  
        next_pobj = (obj*)( (char*)next_pobj 
                            + req_blk_size_align ); 
                            
        // 第 2 ~ req_blk_num - 1 个 blk         
        if (i < req_blk_num  - 2) 
        {
            current_pobj->next = next_pobj; 
        }
        else // 第 req_blk_num 个 blk
        {
            current_pobj->next = 0;
            break;
        }
    }
    return return_pobj;
}

char* __default_alloc::
chunk_alloc(size_t req_blk_size_align, int& req_blk_num )
{
    char* chunk;
    size_t half_blks_bytes_to_get = req_blk_num * req_blk_size_align;
    size_t mem_pool_left = end_free - start_free;

    //(1) mem_pool_left 够 req_blk_num 个 blk -> 分配 req_blk_num 个
    if (mem_pool_left >= half_blks_bytes_to_get)
    {
        chunk = start_free;      
        start_free += half_blks_bytes_to_get;
        return chunk; // 递归出口            
    }
    else if (mem_pool_left > req_blk_size_align) 
    { //(2) 够 n >=1 个 blk => 分配 n 个 
        req_blk_num  = mem_pool_left / req_blk_size_align;   
        half_blks_bytes_to_get = req_blk_num * req_blk_size_align;
        
        chunk = start_free; 
        start_free += half_blks_bytes_to_get;
        return chunk; // 递归出口
    }
    else // (3) 不够 1 个 blk: request from OS, molloc will allocate
    {
        //(4) mem_pool 余量 全 配给 适当 free_list[i]
        if (mem_pool_left > 0)
        {
            // mem_pool 不足 1 个 req_blk:
            // 必然有 刚好能 store 该 req_blk 的 某个 free_list
            // 头插法 insert into 该 free_list
            obj* volatile* my_free_list
                = free_list + freelist_index(mem_pool_left);
                
            ( (obj*)start_free )->next = *my_free_list;
            *my_free_list = (obj*)start_free;
        }
        
        //(5) 向 OS 请求
        size_t bytes_to_get = 2 * half_blks_bytes_to_get 
                            + ROUND_UP( heap_size>>4 );
        start_free = (char*)malloc(bytes_to_get);
        
        if (start_free == 0) 
        {   // heap memory 不足 -> malloc 失败
            // (6) 从 req_blk_size_align 相应 free_list[i] 下一 free_list 开始,
            // 循环遍历 后面 free_list[ j > i ]: 可能含 `unused larger 区块`
            //      larger_blk 必为 req_blk_size_align 整数倍大小
            // 若 有 
            //    (7) 拨 1 个 mem_blk 放 mem_pool
            //    (8) 再 调 自身 chunk_alloc, 向 mem_pool 申请
            obj* volatile * my_free_list, *p;
            for(size_t size = req_blk_size_align;
                size <= MAX_BYTES;
                size+= ALIGN)
            {
                my_free_list
                    = free_list + freelist_index(mem_pool_left);
                p = *my_free_list;
                
                if(p)
                {
                    //(7)
                    *my_free_list = p->next;
                    
                    start_free = (char*)p;
                    end_free = start_free + size;
                    
                    //(8)
                    return ( chunk_alloc(size, req_blk_num) );
                }
            }
            
            //(9)
            end_free = 0; // execute here => there are no memory anywhere
            // 看 第 1级 allocator 的 out_of_memory 是否 有作用
            start_free = (char*)malloc_alloc::allocate(bytes_to_get);   
        }

        //(10) update heap_size
        heap_size += bytes_to_get;

        //(11) update mem_pool tail
        //     head 在 前面已 modify
        end_free = start_free + bytes_to_get;

        //(12) 递归调自己, 以修正 req_blk_num 
        return chunk_alloc(req_blk_size_align, req_blk_num );
    }
}

void __default_alloc::deallocate(void* p, size_t req_blk_size)
{
    if (req_blk_size > (size_t)MAX_BYTES)
    {
        //(1) 调 第1级 allocator
        malloc_alloc::deallocate(p, req_blk_size);
        return;
    }
    
    obj* q = (obj*)p;
    obj* volatile* my_free_list;
    
    my_free_list = free_list 
                 + freelist_index(req_blk_size);

    //(2) recycle the freed mem_blk to free-list
    q->next = *my_free_list;
    *my_free_list = q;
}

//------ 3. wrapper class template: simple_alloc
template<class T, class Alloc>
class simple_alloc
{
public:
    static T*
        allocate(size_t elem_num)
    {
        return 0 == elem_num ? 0 :
            (T *) Alloc::allocate(elem_num * sizeof(T));
    }

    static void
        deallocate(T* p, size_t elem_num)
    {
        if (elem_num != 0)
            Alloc::deallocate(p, elem_num * sizeof(T));
    }
};

// --- data
typedef struct data_32
{
    char mem[32];
}Data_32;

int main()
{
    typedef simple_alloc<Data_32, alloc> data_allocator;
   
    Data_32* p1= data_allocator::allocate(1); // 32 Bytes
    p1 = data_allocator::allocate(2); // 64 Bytes
    p1 = data_allocator::allocate(3); // 96 Bytes
}

4. std::allocator 和 std::alloc 用法

std::vector<int, std::allocator<int> > vec;

// 容器 的 2 个 template para 绑定 在一起
std::vector<int, std::alloc > vec;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容

  • 配置器 空间适配器的标准接口 内存分配释放与构造析构 为了精密分工,STL allocator决定将这两阶段操作区...
    VictorHong阅读 575评论 0 0
  • 第一章 1.9 令人困惑的语法 1.9.1 stl_config.h中的各种组态(configurations) ...
    镜中无我阅读 1,067评论 0 0
  • 2.空间配置器 2.1具备次配置力(sub-allocation)的SGI空间配置器 SGI含有两个空间配置器类,...
    棉花糖7阅读 259评论 0 0
  • 标准配置器 SGI定义有一个符合标准的配置器std::allocator,但这个配置器并没有对内存分配做任何优化,...
    _ace2阅读 331评论 0 0
  • 一、C++对象创建的过程 比如以下的代码 new算式包含两个阶段 调用::operator new 配置内存。 调...
    Catcher07阅读 990评论 0 0