• STL


    STL - 内存分配 - 内存池

    前言

    本文为:STL源码分析第二章《allocator》的阅读记录。

    如果具有很多小额区块的内存分配,那么采用直接分配的方式,不仅会产生很多的碎片内存,对这些空间进行管理也会带来额外的负担。

    文中记录的方法是,如果区块够大,超过128bytes,就向系统直接申请对应的内存空间;当区块小于128bytes的时候,采用内存池进行管理,一次性申请大块的内存空间,用freelist管理剩余空间。为了方便管理,当申请小区块的时候,为将申请的空间上调至8的倍数,16个freelists,管理了大小分别为8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128bytes的小区块。

    完整代码及流程示意图

    #include <iostream>
    #include <stdlib.h>
    #include <memory.h>
    
    namespace {
        
    enum {__ALIGN = 8};
    enum {__MAX_BYTES = 128};
    enum {__NFREELISTS = __MAX_BYTES/__ALIGN};
    
    template <bool threads, int inst/*not used*/>
    class __default_alloc_template {
    private:
        // 将bytes上调至8的倍数
        static size_t ROUND_UP(size_t bytes) {
            return (((bytes) + __ALIGN - 1) & ~(__ALIGN - 1));
        }
    
        union obj {
            union obj * free_list_link;
            char client_data[1];
        };
    
        static obj * volatile free_list[__NFREELISTS];
        static size_t FREELIST_INDEX(size_t bytes) {
            return (((bytes) + __ALIGN - 1) / __ALIGN - 1);
        }
    
        // 当发现list中没有足够的空间的时候,通过refill,
        // 传回一个大小为n的物件,并可能加入大小为n的其他区块到free list
        // 其中n已经上调至8的倍数
        static void* refill(size_t n)
        {
            int nobjs = 20;
            char * chunk = chunk_alloc(n, nobjs);
    
            obj * volatile * my_free_list;
            obj * result;
            obj * current_obj, * next_obj;
    
            if (1 == nobjs) return (chunk);
            
            my_free_list = free_list + FREELIST_INDEX(n);
    
            result = (obj*)chunk;
            *my_free_list = next_obj = (obj*)(chunk + n);
            for (int i = 1; ; i++) {
                current_obj = next_obj;
                next_obj = (obj*)((char*)next_obj + n);
                if (nobjs - 1 == i) {
                    current_obj->free_list_link = 0;
                    break;
                } else {
                    current_obj->free_list_link = next_obj;
                }
            }
            return (result);
        }
    
        // 配置大块空间,可容纳nobjs个大小为“size”的区块
        // 如果,无法配置nobjs个区块,nobjs的值会减小
        static char* chunk_alloc(size_t size, int &nobjs)
        {
            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) {
                // 内存池剩余空间不能完全满足需求量,但足够供应一个以上区块
                nobjs = bytes_left / size;
                total_bytes = size * nobjs;
                result = start_free;
                start_free += total_bytes;
                return result;
            } else {
                // 内存池剩余空间连一个区块大小都无法提供
                size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
                if (bytes_left > 0) {
                    // 内存池汇中还有剩余,先配给适当的freelist
                    obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left);
                    // 调整freelist,将内存池剩余空间编入
                    ((obj*)start_free)->free_list_link = *my_free_list;
                    *my_free_list = ((obj*)start_free);
                }
                
                // 配置heap空间
                start_free = (char*)malloc(bytes_to_get);
                if (0 == start_free) {
                    // heap 空间不足,malloc失败
                    int i;
                    obj * volatile * my_free_list, *p;
                    // 尝试从freelist中查找是不是有足够大的没有用过的区块
                    for (i = size; i <= __MAX_BYTES; i += __ALIGN) {
                        my_free_list = free_list + FREELIST_INDEX(i);
                        p = *my_free_list;
                        if (0 != p) {
                            *my_free_list = p->free_list_link;
                            start_free = (char*)p;
                            end_free = start_free + i;
                            return (chunk_alloc(size, nobjs));
                        }
                    }
                    end_free = 0;
                    start_free = (char*)malloc(bytes_to_get); // 再次尝试,此处原本为malloc_alloc::allocate,内部实现,如果失败后会调用回调,然后再反复尝试
                    if (0 == start_free)
                    {
                        printf("out of memory
    ");
                        exit(1);
                    }
                }
                heap_size += bytes_to_get;
                end_free = start_free + bytes_to_get;
                // 调用自己,回了修正nobjs
                return (chunk_alloc(size, nobjs));
            }
        }
    
        // chunk allocation state
        static char *start_free; // 内存池的起始位置,只在chunk_alloc中变化
        static char *end_free;   // 内存池的结束位置,只在chunk_alloc中变化
        static size_t heap_size;
    
    public:
        static void * allocate(size_t n) {
            obj * volatile * my_free_list;
            obj * result;
    
            // 大于最大值的时候直接分配内存
            if (n > (size_t) __MAX_BYTES)
                return malloc(n); // 此处不用,内存不足时,反复尝试的机制
    
            my_free_list = free_list + FREELIST_INDEX(n);
            result = *my_free_list;
            if (result == nullptr) {
                // 没有找到可用的freelist,准备重新填充freelist
                void *r = refill(ROUND_UP(n));
                return r;
            }
    
            *my_free_list = result->free_list_link;
            return (result);
        }
        static void deallocate(void *p, size_t n)
        {
            obj *q = (obj *)p;
            obj * volatile * my_free_list;
    
            // 大于最大值,直接释放内存
            if (n > (size_t) __MAX_BYTES) {
                free(p);
                return;
            }
            my_free_list = free_list + FREELIST_INDEX(n);
            q->free_list_link = *my_free_list;
            *my_free_list = q;
        }
    };
    
    template <bool threads, int inst>
    typename __default_alloc_template<threads, inst>::obj * volatile 
        __default_alloc_template<threads, inst>::free_list[__NFREELISTS] = {0};
    
    template<bool threads, int inst>
    char *__default_alloc_template<threads, inst>::start_free = 0;
    
    template<bool threads, int inst>
    char *__default_alloc_template<threads, inst>::end_free = 0;
    
    template<bool threads, int inst>
    size_t __default_alloc_template<threads, inst>::heap_size = 0;
    
    }
    
    using My_Alloc = __default_alloc_template<false, 0>;
    
    
    int main()
    {
        int nCount = 4;
        int *a = (int *)My_Alloc::allocate(sizeof(int)*nCount);
        for (int i=0; i<nCount; ++i)
        {
            a[i] = i;
        }
    
        std::cout << "a[](" << a << "): ";
        for (int i=0; i<nCount; ++i)
        {
            std::cout << a[i] << " ";
        }
    
        My_Alloc::deallocate(a, sizeof(int)*nCount);
    
        std::cout << std::endl;
        std::cout << "a[](" << a << "): ";
        for (int i=0; i<nCount; ++i)
        {
            std::cout << a[i] << " ";
        }
    
        return 0;
    }
    

    输出为:

    a[](00175A20): 0 1 2 3
    a[](00175A20): 1530416 1 2 3
    

    其中static void * allocate(size_t n) 对应的示意图如下:

    其中static void deallocate(void *p, size_t n)对应的示意图如下:

    小结

    整个过程对应的示意图如下:

    整体流程大致说明:

    • 一开始有个freelist列表,系统的内存空间;
    • 请求分配内存;
    • 如果分配的内存块很大,直接从系统内存中申请对应大小的内存空间返回;
    • 如果分配的内存块比较小,从freelist中查找,
    • 如果没找到,那么从memory pool中查找特定大小的内存空间,
    • 如果还没找到,那么从系统内存中申请特定大小的内存空间,一部分返回放到memory pool,一部分返回放到freelist,还有一部分返回给内存申请者。

    接下来就可以进一步理解《learn nginx - 共享内存 - 小记》。

  • 相关阅读:
    [Cypress] install, configure, and script Cypress for JavaScript web applications -- part4
    [Angular] Lazy Load CSS at runtime with the Angular CLI
    [置顶] 半年的工作总结
    分享4个未注册*sdn域名
    Windows 已在 DImageProcess.exe 中触发一个断点。
    Foundation: Binary Search
    HDU 3016 Man Down (线段树+dp)
    网络智能和大数据公开课Homework3 Map-Reduce编程
    centos 6.2 关闭 IPV6
    [置顶] 【Git入门之八】分支管理
  • 原文地址:https://www.cnblogs.com/grass-and-moon/p/13690272.html
Copyright © 2020-2023  润新知