空間配置器 & 內存池 設計 & set_new_handler: STL 2.2

從 STL 實現(xiàn)角度 看, 
`allocator 是 STL 其他組件實現(xiàn) 的 基礎`

allocator 稱為 空間配置器, 而不是 內存配置器, 
因為 空間 可以是 內存/磁盤/其他存儲介質

1 仿 SGI std::allocator 設計 1個 符合 STL規(guī)范 的 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
若頻繁申請 小型區(qū)塊, 比如 8Bytes, 
則 cookie 就占了 一半內存
-> `空間利用率低, 且易造成 內存碎片 
-> 解決: 內存池 設計
2. gcc2.91 有 標準分配器 std::allocator 卻不用, 
而是用 special allocator std::alloc

其設計思維為:

(1) 多線程: 本節(jié)不討論
(2) 模擬 C++ set_new_handler, 以處理 內存不足 狀況

(3) 內存池 設計: 以應對 頻繁申請 小型區(qū)塊 時, 空間利用率低 與 內存碎片 問題

兩級 配置器:

第1級 處理 >128 Bytes 區(qū)塊: 仿 set_new_handler

第2級 處理 8*n (n = 1,...,16) <= 128 Bytes 區(qū)塊: 
    16 個 free-list + 內存池

3. 16 個 free-list + 內存池

申請 n <= 128 Bytes 時, 多分配若干 Bytes, 存于

兩級緩存

第 1 / 2 級放 free-list / 內存池, 使得 

下次申請 ( 的 區(qū)塊大小 前面已申請過 ) 時, 可能 直接/間接 從 free-list / 內存池 中 取, 而不是 又 讓 OS 分配

// request memory block 的 process strategy:
if (memory pool 完全滿足需求: <= 20 個 區(qū)塊. 
        若 modify internal_req_obj_num, 則 取 < )
    (1) 提供 所需
else if (memory pool 不完全滿足需求, 但能提供 n >=1 個 區(qū)塊)
    (2) 提供 n 個區(qū)塊
        modify internal_req_obj_num
else // memory pool 連 1個 區(qū)塊 也 提供不了                 
    (3) 先將 memory pool 所有剩余 space 配給 相應 free_list[i]
    (4) 再向 OS 申請 -> malloc 分配 heap memory: bytes_to_get
    若 heap memory 不足 -> malloc 失敗 
        (5) 循環(huán)遍歷 free_list[i, i+1, ...] 中 
        larger( => 必為 req_mem_blk_size_8_align 整數倍 
                => 必 在 更后面的 free_list[j>i] )
        且 unused 區(qū)塊 
        若有 
            (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 區(qū)塊:
    第 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 多線程參數 本節(jié)不考慮 
-> 本文 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 個 區(qū)塊. 若 modify req_obj_num, 則 取 < )
            提供 所需
        else if (memory pool 不完全滿足需求, 但能提供 n >=1 個 區(qū)塊)
            提供 n 個區(qū)塊
                modify req_obj_num
        else // memory pool 連 1個 區(qū)塊 也 提供不了
        {    
            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 開始,
                循環(huán)遍歷 后面 free_list[ j > i ]: 可能含 `unused larger 區(qū)塊`
                {   
                    若 free_list[j] 有 `unused larger 區(qū)塊(必為 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) 若執(zhí)行到 該行 => 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 優(yōu)勢在此十分明顯, 清楚的指出 
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
節(jié)省 該 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 是 連續(xù) memory

用 union 實現(xiàn) 一物二用

使得 管理 list 時, 無需 為 每個 node 單獨分配 pointer 域,
提高了 memory 空間利用率

Java 中 無法實現(xiàn) 該 技巧

(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 優(yōu)化 read/store(write)

(2) 含義:
1) volatile `總是 與 compiler 優(yōu)化 相關`

2) volatile 字面含義是 易變的

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

`多任務 / 中斷` 下, variable 可能被 other program(如 thread) 改變, 
compiler 無法感知到, volatile 就是 告訴 compiler 
不要 在 兩個操作 之間 緩存 variable 到 register/高速cache

2) 對 volatile variable 的 read/write 不會被 優(yōu)化掉

(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 一物二用: 實現(xiàn) 節(jié)省 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 個 區(qū)塊 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 是 連續(xù) 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 開始,
            // 循環(huán)遍歷 后面 free_list[ j > i ]: 可能含 `unused larger 區(qū)塊`
            //      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;
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
禁止轉載松蒜,如需轉載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末古拴,一起剝皮案震驚了整個濱河市配乓,隨后出現(xiàn)的幾起案子败匹,更是在濱河造成了極大的恐慌症杏,老刑警劉巖豫尽,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吼砂,居然都是意外死亡逆航,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門帅刊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纸泡,“玉大人,你說我怎么就攤上這事赖瞒∨遥” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵栏饮,是天一觀的道長吧兔。 經常有香客問我,道長袍嬉,這世上最難降的妖魔是什么境蔼? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮伺通,結果婚禮上箍土,老公的妹妹穿的比我還像新娘。我一直安慰自己罐监,他們只是感情好吴藻,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弓柱,像睡著了一般沟堡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上矢空,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天航罗,我揣著相機與錄音,去河邊找鬼屁药。 笑死粥血,一個胖子當著我的面吹牛,可吹牛的內容都是我干的者祖。 我是一名探鬼主播立莉,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼七问!你這毒婦竟也來了蜓耻?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤械巡,失蹤者是張志新(化名)和其女友劉穎刹淌,沒想到半個月后饶氏,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡有勾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年疹启,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔼卡。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡喊崖,死狀恐怖,靈堂內的尸體忽然破棺而出雇逞,到底是詐尸還是另有隱情荤懂,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布塘砸,位于F島的核電站节仿,受9級特大地震影響,放射性物質發(fā)生泄漏掉蔬。R本人自食惡果不足惜廊宪,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望女轿。 院中可真熱鬧箭启,春花似錦、人聲如沸蛉迹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婿禽。三九已至,卻和暖如春大猛,著一層夾襖步出監(jiān)牢的瞬間扭倾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工挽绩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留膛壹,地道東北人。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓唉堪,卻偏偏與公主長得像模聋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子唠亚,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內容

  • 配置器 空間適配器的標準接口 內存分配釋放與構造析構 為了精密分工链方,STL allocator決定將這兩階段操作區(qū)...
    VictorHong閱讀 534評論 0 0
  • 第一章 1.9 令人困惑的語法 1.9.1 stl_config.h中的各種組態(tài)(configurations) ...
    鏡中無我閱讀 1,010評論 0 0
  • 2.空間配置器 2.1具備次配置力(sub-allocation)的SGI空間配置器 SGI含有兩個空間配置器類,...
    棉花糖7閱讀 244評論 0 0
  • 標準配置器 SGI定義有一個符合標準的配置器std::allocator灶搜,但這個配置器并沒有對內存分配做任何優(yōu)化祟蚀,...
    _ace2閱讀 299評論 0 0
  • 一工窍、C++對象創(chuàng)建的過程 比如以下的代碼 new算式包含兩個階段 調用::operator new 配置內存。 調...
    Catcher07閱讀 973評論 0 0