如何实现内存分配器?

来源:互联网 发布:小米windows平板3 编辑:程序博客网 时间:2024/05/17 02:38

内存分配器(Memory Allocator)负责内存管理,实现动态内存的分配和释放。内存分配器分为两级。第一级分配器直接调用C函数分配内存,第二级分配器则采用内存池来管理内存。如果申请的内存块足够大,那么启动第一级分配器,否则启动第二级分配器。这种设计的优点是可以快速分配和释放小块内存,同时避免内存碎片;缺点是内存池的生命周期比较长,并且很难显式释放。

一些平台对某些特定类型的数据只能从某些特定地址开始存取,这就要求内存分配器可以由使用者指定对齐字节数。在通常情况下,考虑数据类型bool、char、short、int、long long、float、double的最大数据长度为64bit,可以采用8字节对齐,这也是内存分配器的默认对齐参数。但是,使用__m128、__m128i、__m128d时需要16字节对齐,使用__m256则需要32字节对齐。

第一级分配器只是简单的调用函数malloc()、realloc()和free()。为了保证内存按照指定字节数对齐,则需要调用函数_aligned_malloc()、_aligned_realloc()和_aligned_free(),因此实际分配的内存块可能大于申请内存的大小。

第二级分配器需要维护16个空闲块链表和一个内存池。每个链表中的空闲块的大小都是固定的,假定对齐字节数为n,则各个链码空闲块大小依次为n、2n、3n、4n、5n、6n、7n、8n、9n、10n、11n、12n、13n、14n、15n、16n。内存池由两个指针来描述,free_start记录起始地址,free_end记录结束地址。另外两个变量heap_size和used_size分别纪录堆大小和已用内存大小。

内存池管理的内存块大小只有固定的16个规格, 当所需内存块大于16n时,则使用第一级分配器进行内存分配。否则,按照以下步骤进行内存分配:

  1. 申请内存的大小上调至n的倍数,根据此大小查找对应的空闲链表;
  2. 如果空闲链表中有可用的内存块,则直接返回此空闲块,并从空闲链表中删除该块,否则继续下面的步骤;
  3. 计算内存池中所剩空间的大小,如果足以分配16个内存块,则从中取出16个内存块,调整内存池起始地址,返回第一个内存块,并将剩余的15个块并入空闲链表,否则继续下面的步骤;
  4. 如果剩余空间足以分配至少1个内存块,则从中取出尽可能多的内存块,调整内存池起始地址,返回第一个内存块,并将剩余的内存块并入空闲链表,否则继续下面的步骤;
  5. 如果内存池中还有一些内存,则将剩余空间并入其对应大小的空闲链表中;
  6. 向系统申请一个较大的内存块,如果申请成功,返回第一个内存块,调整内存池起始地址,否则继续下面的步骤;
  7. 遍历空闲链表,如果存在更大的空闲内存块,则从空闲链表中删除该块,返回该块首地址,并将剩余的部分内存交给内存池管理,否则分配失败。

内存池向系统申请的内存空间,在使用过程中会被划分为更小的内存块,而这些小内存块的使用和归还几乎是随机的。如果试图对这些小内存块进行合并和释放,其高昂的代价会大幅降低内存池的性能。但在内存池的已用内存大小为0时,释放内存是安全的。内存分配器维护一个指针链表,用于内存空间的统一释放。

内存分配器的对齐字节数决定了空闲链表中的内存块大小,这就意味着对齐字节数不同的分配器所维护的空闲链表是不相同的。因此,对齐字节数相同的分配器被认为是同一个分配器,否则被认为是不同的分配器。这与C++标准中对内存分配器的规定是不一样的,标准中的内存分配器没有考虑内存对齐,所有分配器都被视为相等。这也是C++标准容器不支持有内存对齐要求的数据类型的原因,如:__m128、__m128i、__m128d和__m256等数据。

[cpp] view plain copy
  1. template <size_t align>  
  2. class alloc_base  
  3. {  
  4. private:  
  5.     // 剩余内存节点  
  6.     union free_node  
  7.     {  
  8.         union free_node* next;  
  9.         char buffer[1];  
  10.     };  
  11.     // 内存指针节点  
  12.     struct ptr_node  
  13.     {  
  14.         ptr_node* next;  
  15.         char* ptr;  
  16.     };  
  17. private:  
  18.     static const size_t max_bytes = align << 4;  
  19.     static const size_t list_count = 16;  
  20.     static const size_t chunk_count = 16;  
  21.     static const size_t heap_threshold = (max_bytes * chunk_count) << 5;  
  22.     static const size_t ptr_node_size = (sizeof(ptr_node) + align - 1) & ~(align - 1);  
  23.   
  24.     static free_node* volatile free_list[list_count]; // 空闲内存链表  
  25.     static ptr_node* volatile  ptr_list;              // 指针链表  
  26.     static char*               free_start;            // 内存池起始地址  
  27.     static char*               free_end;              // 内存池结束地址  
  28.     static size_t              heap_size;             // 内存池容量  
  29.     static size_t              used_size;             // 已用内存大小  
  30. private:  
  31.     // 获取对齐大小  
  32.     static size_t round_up(size_t bytes)  
  33.     {  
  34.         return ((bytes + align - 1) & ~(align - 1));  
  35.     }  
  36.     // 获取链表索引  
  37.     static size_t free_list_index(size_t bytes)  
  38.     {  
  39.         return (bytes + align - 1) / align - 1;  
  40.     }  
  41.     // 分配多个内存块  
  42.     static char* chunk_alloc(size_t align_bytes, size_t& count)  
  43.     {  
  44.         char* result = nullptr;  
  45.         size_t memory_size;  
  46.         size_t total_bytes = align_bytes * count;  
  47.   
  48.         // 从堆空间重新分配内存  
  49.         if (heap_size < heap_threshold)  
  50.             memory_size = total_bytes << 1;  
  51.         else  
  52.             memory_size = (heap_size >> 7) << 3;  
  53.         while (memory_size >= total_bytes)  
  54.         {  
  55.             // 从系统获取内存  
  56.             char* ptr = reinterpret_cast<char*> (_aligned_malloc(ptr_node_size + memory_size, align));  
  57.             if (ptr != nullptr)  
  58.             {  
  59.                 result = ptr + ptr_node_size;  
  60.                 free_start = result + total_bytes;  
  61.                 free_end = result + memory_size;  
  62.                 heap_size += memory_size;  
  63.                 // 指针链表  
  64.                 reinterpret_cast<ptr_node*> (ptr)->next = ptr_list;  
  65.                 reinterpret_cast<ptr_node*> (ptr)->ptr = ptr;  
  66.                 ptr_list = reinterpret_cast<ptr_node*> (ptr);  
  67.                 break;  
  68.             }  
  69.             // 请求空间大小减半  
  70.             memory_size >>= 1;  
  71.         }  
  72.         // 如果堆空间无可用内存  
  73.         if (result == nullptr)  
  74.         {  
  75.             // 在空闲链表中搜索可用空间  
  76.             for (size_t size = align_bytes; size <= max_bytes; size += align)  
  77.             {  
  78.                 free_node* volatile* current_list = free_list + free_list_index(size);  
  79.                 if (*current_list != nullptr)  
  80.                 {  
  81.                     count = 1;  
  82.                     result = (*current_list)->buffer;  
  83.                     free_start = result + align_bytes;  
  84.                     free_end = result + size;  
  85.                     break;  
  86.                 }  
  87.             }  
  88.         }  
  89.         return result;  
  90.     }  
  91. public:  
  92.     // 返回容积  
  93.     static size_t capacity(void)  
  94.     {  
  95.         return heap_size;  
  96.     }  
  97.   
  98.     // 返回大小  
  99.     static size_t size(void)  
  100.     {  
  101.         return used_size;  
  102.     }  
  103.   
  104.     // 分配内存  
  105.     static void* allocate(size_t size)  
  106.     {  
  107.         // 一级分配器  
  108.         if (size > max_bytes)  
  109.         {  
  110.             void* result = _aligned_malloc(size, align);  
  111.             if (result == nullptr)  
  112.                 throw std::bad_alloc();  
  113.             return result;  
  114.         }  
  115.         // 二级分配器  
  116.         else  
  117.         {  
  118.             char *result = nullptr;  
  119.             size_t count = chunk_count;  
  120.             free_node* volatile* current_list = free_list + free_list_index(size);  
  121.   
  122.             // 在空闲链表中搜索可用空间  
  123.             if (*current_list != nullptr)  
  124.             {  
  125.                 result = (*current_list)->buffer;  
  126.                 *current_list = (*current_list)->next;  
  127.             }  
  128.             // 在内存池中搜索可用空间  
  129.             else  
  130.             {  
  131.                 size_t align_bytes = round_up(size);  
  132.                 size_t total_bytes = align_bytes * count;  
  133.                 size_t free_bytes = free_end - free_start;  
  134.   
  135.                 if (free_bytes >= total_bytes)  
  136.                 {  
  137.                     result = free_start;  
  138.                     free_start += total_bytes;  
  139.                 }  
  140.                 else if (free_bytes >= align_bytes)  
  141.                 {  
  142.                     count = free_bytes / align_bytes;  
  143.                     total_bytes = align_bytes * count;  
  144.                     result = free_start;  
  145.                     free_start += total_bytes;  
  146.                 }  
  147.                 else  
  148.                 {  
  149.                     // 将剩余内存编入空闲链表  
  150.                     if (free_bytes > 0)  
  151.                     {  
  152.                         free_node* volatile* free_list_left = free_list + free_list_index(free_bytes);  
  153.                         reinterpret_cast<free_node*> (free_start)->next = *free_list_left;  
  154.                         *free_list_left = reinterpret_cast<free_node*> (free_start);  
  155.                         free_start = free_end;  
  156.                     }  
  157.                     // 分配多个内存块  
  158.                     result = chunk_alloc(align_bytes, count);  
  159.                 }  
  160.                 // 填充空闲链表  
  161.                 if (result != nullptr && count > 1)  
  162.                 {  
  163.                     char *cur, *next = result + align_bytes;  
  164.                     *current_list = reinterpret_cast<free_node*> (next);  
  165.                     for (size_t i = 2; i < count; ++i)  
  166.                     {  
  167.                         cur = next;  
  168.                         next += align_bytes;  
  169.                         reinterpret_cast<free_node*> (cur)->next = reinterpret_cast<free_node*> (next);  
  170.                     }  
  171.                     reinterpret_cast<free_node*> (next)->next = nullptr;  
  172.                 }  
  173.             }  
  174.             if (result != nullptr)  
  175.                 used_size += size;  
  176.             else  
  177.                 throw std::bad_alloc();  
  178.             return reinterpret_cast<void*> (result);  
  179.         }  
  180.     }  
  181.   
  182.     // 释放内存  
  183.     static void deallocate(void* ptr, size_t size)  
  184.     {  
  185.         if (size > max_bytes)  
  186.         {  
  187.             _aligned_free(ptr);  
  188.         }  
  189.         else  
  190.         {  
  191.             free_node* volatile* current_list = free_list + free_list_index(size);  
  192.             reinterpret_cast<free_node*> (ptr)->next = *current_list;  
  193.             *current_list = reinterpret_cast<free_node*> (ptr);  
  194.             used_size -= size;  
  195.         }  
  196.     }  
  197.   
  198.     // 重新分配内存  
  199.     static void* reallocate(void *ptr, size_t old_size, size_t new_size)  
  200.     {  
  201.         void *result = nullptr;  
  202.         size_t copy_size;  
  203.   
  204.         if (old_size > max_bytes && new_size > max_bytes)  
  205.         {  
  206.             result = _aligned_realloc(ptr, new_size, align);  
  207.         }  
  208.         if (round_up(old_size) == round_up(new_size))  
  209.         {  
  210.             result = ptr;  
  211.             used_size -= old_size;  
  212.             used_size += new_size;  
  213.         }  
  214.         else  
  215.         {  
  216.             result = allocate(new_size);  
  217.             if (result == nullptr)  
  218.             {  
  219.                 copy_size = new_size > old_size ? old_size : new_size;  
  220.                 memcpy(result, ptr, copy_size);  
  221.                 deallocate(ptr, old_size);  
  222.             }  
  223.         }  
  224.         if (result == nullptr)  
  225.             throw std::bad_alloc();  
  226.         return result;  
  227.     }  
  228.   
  229.     // 释放全部内存  
  230.     static void release(void)  
  231.     {  
  232.         if (used_size != 0)  
  233.             return;  
  234.         while (ptr_list != nullptr)  
  235.         {  
  236.             ptr_node *next = ptr_list->next;  
  237.             _aligned_free(ptr_list->ptr);  
  238.             ptr_list = next;  
  239.         }  
  240.         heap_size = 0;  
  241.         used_size = 0;  
  242.     }  
  243. };  
0 0
原创粉丝点击