stl源码——内存配置器

一级配置器

class __malloc_alloc
{
    using malloc_handler = void(*)();  // 函数指针

private:
    // 用于处理内存不足的情况
    static void *oom_malloc(size_t);
    static void *oom_realloc(void* , size_t);
    // 处理函数
    static malloc_handler __malloc_alloc_oom_handler;

public:
    static void *allocate(size_t n)
    {
        // 分配器
        void *result = malloc(n);
        if (result == nullptr)
        {
            // 申请失败,处理内存不足的情况,使用oom_malloc函数再次申请
            oom_malloc(n);
        }
        return result;
    }

    static void* deallocate(void* p, size_t n)
    {
        free(p);
    }

    static void* reallocate(void * p, size_t, size_t new_size)
    {
        void* result = realloc(p, new_size); // 内置的 在p的位置申请size大小
        if (result == nullptr)
        {
            // 空间不足,使用oom_realloc重新分配
            oom_realloc(p, new_size);
        }
    }
};
// 函数指针指向空,不处理
typename __malloc_alloc::malloc_handler 
    __malloc_alloc::__malloc_alloc_oom_handler = nullptr;
// allocate 申请失败时的处理函数 oom_malloc函数的实现
void * __malloc_alloc::oom_malloc(size_t n)
{
    // 再次申请
    malloc_handler new_alloc_handler;
    void * result;
    for(;;)
    {
        // 循环尝试?
        new_alloc_handler = __malloc_alloc_oom_handler;
        if (new_alloc_handler == nullptr)
        {
            throw std::bad_alloc();
        }
        (*new_alloc_handler)(); // 调用函数
        result = malloc(n);
        if (result != nullptr)
        {
            return result;
        }
    }
}

// realloc失败处理函数
void * __malloc_alloc::oom_realloc(void* p, size_t n)
{
    malloc_handler new_alloc_handler;
    void * res;
    for (;;)
    {
        new_alloc_handler = __malloc_alloc_oom_handler;
        if (new_alloc_handler == nullptr)
        {
            throw std::bad_alloc();
        }
        (*new_alloc_handler)();
        res = realloc(p, n);
        if (res != nullptr)
        {
            return res;
        }
    }
}
using malloc_alloc = __malloc_alloc; // 一级配置器

二级配置器

// freelist参数设定
enum __freelist_setting
{
    __ALIGN = 8,
    __MAX_BYTES = 128,
    __NFREELISTS = __MAX_BYTES / __ALIGN
};

class __default_alloc
{
private:
    // 将字节转换为8的倍数,向上(大)取 ---- 使用计算器尝试
    // 7bytes
    // 7 + 7   &  ~7  14 & ~7    1110 & ~111   1110 &1000 = 8 即第一个链表
    // 15bytes
    // 15 + 7  & ~7  22 & ~7   10110 & 00000 = 16  及第二个链表

    // 22bytes
    // 22 + 7 = 29 & ~7  11101& 11000 = 11000 = 24

    // 30bytes 
    // 37 & ~7  100101 & 111000 = 100000 = 32 
    static size_t ROUND_UP(size_t bytes)
    {
        return (((bytes)  + static_cast<size_t>(__ALIGN) -1) & 
            ~(static_cast<size_t>(__ALIGN) -1) );
    }

private:
    // free_list 节点
    // 由于union特性 并不需要占用额外空间
    union obj
    {
        union obj* free_list_link; // 指向下一个节点
        char clinet_data[1]; // 指向资源
    };

private:
    static obj* volatile free_list[__NFREELISTS]; // vol  表示存放在内存中可变
    
    static size_t FREELIST_INDEX(size_t bytes)
    {
        return (bytes + static_cast<size_t>(__ALIGN) - 1) / 
            static_cast<size_t>(__ALIGN) - 1;
        
        // 3 bytes  3 + 7 = 10  / 8  - 1 = 0  
        // 13 bytees 13 + 7 = 20 / 8 - 1 = 1
        // 23 BYTES 23 + 7 = 30 / 8 - 1 = 2
        // 100 BYTES = 107 / 8 - 1 = 12
    }

    static void* refill(size_t n); // freelist没有可用空间时操作

    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); // 分配n大小的空间
    static void deallocate(void *p , size_t n); // 释放p处n大小的空间
    static void* reallocate(void * p, size_t old_sz, size_t new_sz);  // 将p处old大小释放,申请new大小
};

实现

char* __default_alloc::start_free = nullptr; // 内存池初始  
char* __default_alloc::end_free = nullptr;
size_t __default_alloc::heap_size = 0;

__default_alloc::obj * volatile __default_alloc::free_list[__NFREELISTS] = {
    nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,nullptr,
    nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,nullptr
};

// freelist无可用区块的时候, 重新填充空间,
// 新空间取自内存池, 默认获取20个节点
// 若内存池不足,则获取的将小于20  获取20 * n个大小
void * __default_alloc::refill(size_t n)
{
    // 这里的n就是8 16 24 32 。。。 各个区块的大小
    int nobjs = 20; // obj区块
    char * chunk = chunk_alloc(n, nobjs); // chunk_alloc() 区块分配 n默认是8的倍数, nobjs使用引用接受,需要修改
    obj* volatile *my_free_list;
    obj* result;
    obj* current_obj, *next_obj;

    // 只能搞到一块这么大的 直接返回
    if (nobjs == 1)
    {
        return (chunk);
    }
    // 不止一块
    my_free_list = free_list + FREELIST_INDEX(n); // 回到该链表的头结点处
    result = reinterpret_cast<obj*>(chunk); // 将申请到的结果转化为块

    // 下述操作是将这些块连接起来
    *my_free_list = next_obj = reinterpret_cast<obj*>(chunk + n);  // 取走n之后指针所指
    for (int i = 1;; i++)
    {
        // 循环,模拟不断申请
        current_obj = next_obj;
        next_obj = 
            reinterpret_cast<obj*>(reinterpret_cast<char*>(next_obj) + n); // 一块
        if (nobjs - 1 == i)
        {
            // 取走20块,
            current_obj->free_list_link = nullptr; 
            break;
        }
        else
        {
            // 使用指针串连上
            current_obj->free_list_link = next_obj;
        }
    }
    return result;
}

char* __default_alloc::chunk_alloc(size_t size, int& nobjs)
{
    // 开辟nobjs个 size(8 16 24...) 大小的空间
    char* result;
    size_t total_bytes = size * nobjs;
    size_t bytes_left = end_free - start_free; // 内存池剩余空间
    if (bytes_left >= total_bytes)
    {
        // 内存池足够支撑分配,直接从内存池获取
        result = start_free;
        start_free += total_bytes;
        return result;
    }
    else if (bytes_left > size) // 我的想法的>=
    {
        // 内存池容量不够20 个, 但是足够一个以上,将这些块全部获取了,更新内存池头尾
        nobjs = static_cast<int>(bytes_left / size); // 内存池中的该块实际个数
        total_bytes = size * nobjs;
        result = start_free;
        start_free = start_free + total_bytes;
        return result;
    }
    else
    {
        // 内存池仅有小于一个的大小
        // 申请空间一般大小是size*nobjs * 2 + random
        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
        // heapsize会随着申请的次数逐渐增加

        if (bytes_left > 0)
        {
            // 内存池还有,但是不够1块,不要浪费了,
            obj* volatile *my_free_list = free_list + FREELIST_INDEX(bytes_left); // 定位到这块开头
            reinterpret_cast<obj*>(start_free)->free_list_link = *my_free_list;  // 从不浪费的那里作为内存池的开头
            *my_free_list = reinterpret_cast<obj*>(start_free); // 以块的形式
        }
        start_free = reinterpret_cast<char*>(malloc(bytes_to_get));
        if (start_free == nullptr)
        {
            // 堆空间不足以分配
            obj* volatile *my_free_list;
            obj* p;
            
            // 检查其他拉链中有没有<=128bytes的区块
            for (size_t i = 0; i < static_cast<size_t>(__MAX_BYTES); i += static_cast<size_t>(__ALIGN))
            {
                my_free_list = free_list + FREELIST_INDEX(i); // 遍历每一个头
                p = *my_free_list;
                if (p != nullptr)
                {
                    // 有拉链 将拉链中的空间提取出来
                    *my_free_list = p->free_list_link;
                    start_free = reinterpret_cast<char*>(p);
                    end_free = start_free + i;
                    return (chunk_alloc(size, nobjs)); // 递归调用,修正nobjs 此时必然进入else if 分支
                }
            }
            end_free = nullptr; // 到处都找不到内存
            // 调用一级配置器
            start_free = reinterpret_cast<char*>(malloc_alloc::allocate(bytes_to_get));
        }
        // 分配了堆空间,大小够不够递归跑一下
        heap_size += bytes_to_get; // 已占用堆的内存大小
        end_free = start_free + bytes_to_get;
        return chunk_alloc(size, nobjs); // 调用自身修正nobjs
    }
}
void * __default_alloc::allocate(size_t n)
{
    obj * volatile * myfreelist;
    obj * result;
    if (n > __MAX_BYTES)
    {
        // 大于128字节  使用一级配置器
        return (malloc_alloc::allocate(n));
    }

    // 二级配置器
    // 链表是类似hash的拉链法,将其他20个节点以链表的形式 挂到每个节点上
    myfreelist = free_list + FREELIST_INDEX(n); // 链表首地址 + 下标
    
    // myfreelist就是记录大hash表中,拉链的头节点地址
    result = *myfreelist;
    if (result == nullptr)
    {
        // 说明该拉链没有空间,需要填充
        void * r = refill(ROUND_UP(n)); // 分配n 比如我要34bytes 那么是从40bytes为一块这里进行填充,填充20个
        return r;
    }
    // 该处不需要填充,直接从里面取一块空间
    *myfreelist = result->free_list_link; // 指向这一块的链表区
    return result;
}

// 回收p
void  __default_alloc::deallocate(void* p, size_t n)
{
    if (n > static_cast<size_t>(__MAX_BYTES))
    {
        malloc_alloc::deallocate(p, n); // 如果要申请的n过大,使用一级配置器
    }
    else
    {
        obj* volatile *my_free_list = free_list + FREELIST_INDEX(n);
        obj* q = reinterpret_cast<obj*>(p);
        q->free_list_link = *my_free_list; // 指向头  就回收了
        *my_free_list = q;
    }
}

void *__default_alloc::reallocate(void* p, size_t old_sz, size_t new_sz)
{
    void* result ;
    size_t copy_sz;
    // 都是128以上的,使用一级配置
    if (old_sz > static_cast<size_t>(__MAX_BYTES)
        && new_sz > static_cast<size_t>(__MAX_BYTES))
    {
        return realloc(p, new_sz);
    }

    // 如果这两个的区块大小相同,即在同一个拉链上
    if (ROUND_UP(old_sz) == ROUND_UP(new_sz))
    {
        return p;
    }

    result = allocate(new_sz);  // 配置newsize大小
    copy_sz = new_sz > old_sz ? old_sz : new_sz;  // 小的
    memcpy(result, p, copy_sz);  // 原来的都拷贝过来,多了的不要
    deallocate(p, old_sz); // 二级配置器的p位置,回收oldsz大小
    return result;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值