c++ 内存管理 2 allocator——pool_allocator

这篇博客详细介绍了如何使用C++实现一个内存管理器,包括一个基于malloc的allocator和一个基于内存池的pool_alloc。malloc_alloc封装了malloc和free,而pool_alloc通过维护不同大小的free_list来提高小块内存的分配效率。当内存池不足以满足需求时,pool_alloc会尝试从堆中分配更多内存或者调用malloc_alloc。博客还涉及到内存不足时的处理机制以及内存对齐等概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

std::allocator

GCC

    // 封装malloc的allocator
    class malloc_alloc
    {
    private:
        static void *oom_malloc(size_t);
        static void (*malloc_alloc_oom_handler)();
    public:
        // 直接使用malloc()
        static void * allocate(size_t n)
        {
            void *result = malloc(n);
            if (0 == result)    result = oom_malloc(n); // 无法malloc,改用oom_malloc
            return result;
        }

        // 直接使用free()
        static void deallocate(void *p)
        {
            free(p);
        }

        // 仿真C++的set_new_handler()
        static void (*set_malloc_handler(void (*f)()))()
        {
            void (*old)() = malloc_alloc_oom_handler;
            malloc_alloc_oom_handler = f;
            return old;
        }
    };

    void (* malloc_alloc::malloc_alloc_oom_handler)() = 0;

    void * malloc_alloc::oom_malloc(size_t n)
    {
        void (*my_malloc_handler)();
        void *result;

        for (;;) {
            my_malloc_handler = malloc_alloc_oom_handler;
            if (0 == my_malloc_handler) { THROW_BAD_ALLOC(); }
            (*my_malloc_handler)(); // 调用处理函数
            result = malloc(n);     // 再次尝试分配内存
            if (result) return result;
        }
    }

    enum { ALIGN = 8 }; // 小型区块的上调边界
    enum { MAX_BYTES = 128 };   // 小型区块的上限
    enum { LEN_FREE_LIST = MAX_BYTES / ALIGN };  // free_list节点个数

    // free_lists节点
    union FreeNode
    {
        union FreeNode *next;   // 指向下一个区块
        char data[1];           // 本快内存首地址
    };

    // 基于内存池的allocator
    class pool_alloc
    {
    private:
        // 将bytes上调至ALIGN的倍数
        static size_t ROUND_UP(size_t bytes)
        { return (bytes + ALIGN - 1) & ~(ALIGN - 1); }

        // 16个节点的free_list
        static FreeNode * free_list[LEN_FREE_LIST];

        // 根据bytes大小,决定使用free_list的第n号区块
        static size_t FREE_LIST_INDEX(size_t bytes)
        { return (bytes + ALIGN - 1) / ALIGN - 1; }

        // allocate()中调用,返回大小为size的空间地址
        // 并可能将多个大小为size的其它区块填充到free_list中
        static void * refill(size_t size);

        // refill()中调用,配置一大块空间,可容纳n_nodes个大小为size的区块
        // 若空间不足,n_nodes可能会降低(传引用)
        static char * chunk_alloc(size_t size, int &n_nodes);

        // 内存池状态
        static char *start;         // 内存池起始位置
        static char *end;           // 内存池结束位置
        static size_t heap_size;    // chunk_alloc时如果从heap空间中获取内存给内存池,则记录这个heap空间大小

    public:
        static void * allocate(size_t n);
        static void deallocate(void *p, size_t n);
    };

    // 初值
    char *pool_alloc::start = nullptr;
    char *pool_alloc::end = nullptr;
    size_t pool_alloc::heap_size = 0;
    FreeNode * pool_alloc::free_list[LEN_FREE_LIST] = {
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0
    };

    void * pool_alloc::allocate(size_t n)
    {
        // 大于MAX_BYTES就调用一级配置器
        if (n > static_cast<size_t>(MAX_BYTES)) {
            return malloc_alloc::allocate(n);
        }
        // 寻找free_list中适当的区块
        FreeNode **my_free_node = free_list + FREE_LIST_INDEX(n);
        FreeNode *result = *my_free_node;
        if (nullptr == result) {
            // 没找到可用的free_list,将n上调,重新填充并返回
            return refill(ROUND_UP(n));
        }
        *my_free_node = result->next;
        return result;
    };

    void pool_alloc::deallocate(void *p, size_t n)
    {
        // 大于MAX_BYTES就调用一级配置器
        if (n > static_cast<size_t>(MAX_BYTES)) {
            malloc_alloc::deallocate(p);
            return;
        }
        FreeNode *q = reinterpret_cast<FreeNode *>(p);
        FreeNode **my_free_node = free_list + FREE_LIST_INDEX(n);
        q->next = *my_free_node;
        *my_free_node = q;
    }

    // free_list无可用时调用,为free_list填充空间
    // 新的空间取自内存池,取得20个新节点
    // 若内存池空间不足,则获得节点数会<20
    void * pool_alloc::refill(size_t size)
    {
        // size已经上调至ALIGN的倍数
        int n_nodes = 20;

        // 取得n_nodes个区块作为free_list的新节点
        char *chunk = chunk_alloc(size, n_nodes);

        if (1 == n_nodes)   return chunk;   // 只获得一个区块,直接返回,没有为free_list填充新空间

        // 否则为free_list纳入新节点
        FreeNode **my_free_node = free_list + FREE_LIST_INDEX(size);
        // chunk空间中第0个返回,第1 ~ n_nodes - 1个填充进free_list并串接起来
        FreeNode *result = reinterpret_cast<FreeNode *>(chunk); // 选择一块size大小的空间返回
        // chunk中剩余空间填充进free_list
        FreeNode *current_node, *next_node;
        *my_free_node = next_node = reinterpret_cast<FreeNode *>(chunk + size);
        for (int i = 1; ; ++i) {
            current_node = next_node;
            next_node = reinterpret_cast<FreeNode *>(reinterpret_cast<char *>(next_node) + size);
            if (n_nodes - 1 == i) {
                current_node->next = nullptr;
                break;
            } else {
                current_node->next = next_node;
            }
        }
        return result;
    }

    // 从内存池取空间给free_list使用,size已上调至ALIGN的倍数
    char * pool_alloc::chunk_alloc(size_t size, int &n_nodes)
    {
        char *result;
        size_t total_bytes = size * n_nodes;
        size_t left_bytes = end - start;    // 内存池剩余空间

        if (left_bytes >= total_bytes) {
            // 剩余空间满足需求量
            result = start;
            start += total_bytes;
            return result;
        } else if (left_bytes >= size) {
            // 剩余空间不满足需求量,但能提供大于等于1个区块
            n_nodes = left_bytes / size;    // 修改n_nodes
            total_bytes = size * n_nodes;
            result = start;
            start += total_bytes;
            return result;
        } else {
            // 剩余空间不够提供1个区块
            // 准备从堆中获取2倍需求量+附加量的空间
            size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
            // 将内存池的残余零头配给free_list中适当的节点
            if (left_bytes > 0) {
                FreeNode **my_free_node = free_list + FREE_LIST_INDEX(left_bytes);
                reinterpret_cast<FreeNode *>(start)->next = *my_free_node;
                *my_free_node = reinterpret_cast<FreeNode *>(start);
            }
            // 配置heap空间,补充内存池
            start = (char *)malloc(bytes_to_get);

            // heap空间不足,malloc失败
            if (nullptr == start) {
                // 从free_list中获取"尚未使用且足够大(>= size)"的区块,分给内存池
                for (int i = size; i <= MAX_BYTES; i += ALIGN) {
                    FreeNode **my_free_node = free_list + FREE_LIST_INDEX(i);
                    FreeNode *p = *my_free_node;
                    if (nullptr != p) {  // free_list中有合适区块
                        *my_free_node = p->next;
                        start = reinterpret_cast<char *>(p);
                        end = start + i;
                        // 内存池中又获得一些空间,递归调用自己,重新从内存池中取空间
                        // 同时修正n_nodes
                        return chunk_alloc(size, n_nodes);
                    }
                }
                // free_list中也无可用区块,调用一级配置器,求助out_of_memory机制
                end = nullptr;
                start = (char *)malloc_alloc::allocate(bytes_to_get);
            }

            // heap空间配置成功 or 使用一级配置器配置heap空间成功 时
            heap_size += bytes_to_get;
            end = start + bytes_to_get;
            // 内存池中又获得一些空间,递归调用自己,重新从内存池中取空间
            // 同时修正n_nodes
            return chunk_alloc(size, n_nodes);
        }
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值