zoukankan      html  css  js  c++  java
  • STL源码剖析(空间配置器)

    前言

    在STL中,容器的定义中都带一个模板参数,如vector

    template <class T, class Alloc = alloc>
    class vector {...}

    其中第二个参数就是该容器使用的空间配置器,其中缺省使用STL已经实现的空间配置器(alloc),

    该配置器使用malloc/free等为vector分配内存。

    缺省的空间配置器

    alloc定义了两级的空间配置器,第一级是对malloc/free简单的封装。

    而为了解决内存碎片的问题,跟进行内存管理,alloc实现的第二级的空间配置器。

    第二级空间配置器在分配大块内存(大于128bytes)时,会直接调用第一级空间配置器,

    而分配小于128bytes的内存时,则使用内存池跟free_list进行内存分配/管理。

    第一级空间配置器

    基本实现如下(跟SGI STL可能有点出入,主要是提取核心的内容)

     1 class base_alloc {
     2 public:
     3     // 只是对malloc/free的简单封装
     4     static void* allocate(size_t n)
     5     {
     6         void* res = malloc(n);
     7         if (0 == res) res = oom_malloc(n);
     8         return res;
     9     }
    10     static void* reallocate(void* p, size_t new_sz)
    11     {
    12         void* res = realloc(p, new_sz);
    13         if (0 == res) res = oom_realloc(p, new_sz);
    14         return res;
    15     }
    16     static void deallocate(void* p)
    17     {
    18         free(p);
    19     }
    20     // 用来设置内存不足时的处理函数 该函数参数跟返回值都是一个函数指针
    21     // 一般会抛出异常/尝试回收内存
    22     static void(*set_handler(void(*f)()))()
    23     {
    24         void(*old)() = _oom_handler;
    25         _oom_handler = f;
    26         return old;
    27     }
    28 private:
    29     // 用来处理内存不足的情况
    30     static void* oom_malloc(size_t n)
    31     {
    32         void(*my_handler)();
    33         void* res;
    34 
    35         for (;;)
    36         {
    37             my_handler = _oom_handler;
    38             if (0 == my_handler) { return NULL; }
    39             (*my_handler)();
    40             if (res = malloc(n)) return res;
    41         }
    42     }
    43     // 用来处理内存不足的情况
    44     static void* oom_realloc(void* p, size_t n)
    45     {
    46         void(*my_handler)();
    47         void* res;
    48 
    49         for (;;)
    50         {
    51             my_handler = _oom_handler;
    52             if (0 == my_handler) { return NULL; }
    53             (*my_handler)();
    54             if (res = reallocate(p, n)) return res;
    55         }
    56     }
    57     // 由用户设置,在内存不足的时候进行处理,由上面两个函数调用
    58     static void(*_oom_handler)();
    59 };
    60 
    61 // 处理函数默认为0 
    62 void(*base_alloc::_oom_handler)() = 0;
    View Code

    它可以设定一个处理内存不足的时候的处理函数(跟set_new_handler类似)。

    第二级空间配置器

    该配置器维护一个free_list,这是一个指针数组。

    在分配内存的时候,补足8bytes的倍数,free_list数组中每个指针分别管理分配大小为8、16、24、32...bytes的内存。

    下图表示从二级空间配置器中分配内存时是如何维护free_list的(建议参考下面源码阅读)。

    开始所有指针都为0,没有可分配的区块时(就是free_list[i]==0)会从内存池中分配内存(默认分配20个区块)插入到free_list[i]中。

    然后改变free_list[i]的指向,指向下一个区块(free_list_link指向下一个区块,如果没有则为0)。

     

    下图表示二级空间配置器回收内存时是如何维护free_list结构的。

    回收的时候只是将区块插入到free_list[i]的开头,这块内存用于下次分配的时候使用。

    该配置器实现如下(同样提取核心的部分):

      1 enum { _ALIGN = 8 };    // 对齐
      2 enum { _MAX_BYTES = 128 }; // 区块大小上限
      3 enum { _NFREELISTS = _MAX_BYTES / _ALIGN }; // free-list个数
      4 
      5 class default_alloc {
      6 private:
      7     // 将bytes上调到8的倍数
      8     static size_t ROUND_UP(size_t bytes)
      9     {
     10         return (bytes + _ALIGN - 1) & ~(_ALIGN - 1);
     11     }
     12 private:
     13     union obj {
     14         union obj* free_list_link;
     15         char client_data[1];
     16     };
     17 private:
     18     // 16个free-lists 各自管理分别为8,16,24...的小额区块
     19     static obj* free_list[_NFREELISTS];
     20     // 根据区块大小,决定使用第n号free-list
     21     static size_t FREELIST_INDEX(size_t bytes)
     22     {
     23         return (bytes + _ALIGN - 1) / _ALIGN - 1;
     24     }
     25     // 分配内存,返回一个大小为n的区块,可能将大小为n的其他区块加入到free_list
     26     static void* refill(size_t n)
     27     {
     28         // 默认分配20个区块
     29         int nobjs = 20;
     30         char* chunk = chunk_alloc(n, nobjs);
     31         obj** my_free_list;
     32         obj* result, *current_obj, *next_obj;
     33 
     34         // 如果只分配了一个区块,直接返回
     35         if (1 == nobjs) return chunk;
     36         // 否则将其他区块插入到free list
     37         my_free_list = free_list + FREELIST_INDEX(n);
     38         result = (obj*)chunk;
     39         // 第一个区块返回 后面的区块插入到free list
     40         *my_free_list = next_obj = (obj*)(chunk + n);
     41         for (int i = 1;; ++i)
     42         {
     43             current_obj = next_obj;
     44             next_obj = (obj*)((char*)next_obj + n);
     45             // 最后一个next的free_list_link为0
     46             if (nobjs - 1 == i)
     47             {
     48                 current_obj->free_list_link = 0;
     49                 break;
     50             }
     51             current_obj->free_list_link = next_obj;
     52         }
     53         return result;
     54     }
     55     // 分配内存
     56     // 在内存池容量足够时,只调整start_free跟end_free指针
     57     // 在内存池容量不足时,调用malloc分配内存(2 * size * nobjs +  ROUND_UP(heap_size >> 4),每次调整heap_size += 本次分配内存的大小) 
     58     static char* chunk_alloc(size_t size, int& nobjs);
     59 
     60     static char* start_free; //内存池的起始位置
     61     static char* end_free;     //内存池的结束位置
     62     static size_t heap_size;         //分配内存时的附加量
     63 public:
     64     static void* allocate(size_t n)
     65     {
     66         obj** my_free_list;
     67         obj* result;
     68         // 大于128就调用第一级空间配置器
     69         if (n > (size_t)_MAX_BYTES)
     70             return base_alloc::allocate(n);
     71 
     72         // 寻找适当的free-list
     73         my_free_list = free_list + FREELIST_INDEX(n);
     74         result = *my_free_list;
     75         // 没有可用的free list
     76         if (result == 0)
     77             return refill(ROUND_UP(n));
     78 
     79         // 调整free list 移除free list
     80         *my_free_list = result->free_list_link;
     81         return result;
     82     }
     83     static void deallocate(void* p, size_t n)
     84     {
     85         obj* q = (obj*)p;
     86         obj** my_free_list;
     87 
     88         // 大于128就调用第一级空间配置器
     89         if (n > (size_t)_MAX_BYTES)
     90         {
     91             base_alloc::deallocate(p);
     92             return;
     93         }
     94 
     95         // 寻找对应的free list
     96         my_free_list = free_list + FREELIST_INDEX(n);
     97         // 调整free list 回收区块(将区块插入到my_free_list)
     98         q->free_list_link = *my_free_list;
     99         *my_free_list = q;
    100     }
    101     static void reallocate(void* p, size_t old_sz, size_t new_sz);
    102 };
    103 
    104 char* default_alloc::start_free = 0;
    105 char* default_alloc::end_free = 0;
    106 size_t default_alloc::heap_size = 0;
    107 default_alloc::obj* default_alloc::free_list[_NFREELISTS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    View Code
  • 相关阅读:
    【洛谷】P1303 A*B Problem(高精度乘法模板)
    快速幂
    【洛谷】P1601 A+B Problem 高精(高精度加法模板)
    进制转换
    【洛谷】P1551 亲戚(并查集模板)
    求最大公约数的两种方法
    快速排序
    异或交换两个数
    数字字符串互相转换的三种方法
    Hello world(我来啦)
  • 原文地址:https://www.cnblogs.com/runnyu/p/5986709.html
Copyright © 2011-2022 走看看