SGI STL 空间配置器(allocator)源码剖析

空间配置器的作用

我们知道,stl中的数据都是放到容器中的,容器需要存储空间,空间配置器就是负责容器存储空间的分配、回收等一系列内存操作。

STL标准规范中描述的allocator模版类的必要声明:

[cpp]  view plain copy
  1. <strong>Constructors</strong>  
  2. // Constructors used to create allocator objects.  
  3. allocator(); // Default constructor   
  4. allocator(const allocator<Type>& _Right);  // Copy constructor  
  5. template<class Other>                      // Template copy constructor  
  6. allocator(const allocator<Other>& _Right);  
  7.   
  8. <strong>Typedefs</strong>  
  9. const_pointer   // A type that provides a constant pointer to the type of object managed by the allocator.   
  10. const_reference // A type that provides a constant reference to type of object managed by the allocator.   
  11. difference_type // A signed integral type that can represent the difference between values of pointers to the type of object managed by the allocator.   
  12. pointer         // A type that provides a pointer to the type of object managed by the allocator.   
  13. reference       // A type that provides a reference to the type of object managed by the allocator.  
  14. size_type       // An unsigned integral type that can represent the length of any sequence that an object of template class allocator can allocate.   
  15. value_type      // A type that is managed by the allocator.  
  16.    
  17. <strong>Member Functions</strong>  
  18. // Finds the address of an object whose value is specified.  
  19. pointer (reference _Val) const;  
  20. const_pointer (const_reference _Val) const;  
  21.   
  22. // Allocates a block of memory large enough to store at least some specified number of elements.  
  23. pointer allocate(size_type _Count, const void* _Hint);  
  24.    
  25. // Constructs a specific type of object at a specified address that is initialized with a specified value.  
  26. void construct(pointer _Ptr, const Type& _Val);  
  27.    
  28. // Frees a specified number of objects from storage beginning at a specified position.  
  29. void deallocate(pointer _Ptr, size_type _Count);  
  30.    
  31. // Calls an objects destructor without deallocating the memory where the object was stored.  
  32. void destroy(pointer _Ptr);  
  33.    
  34. // Returns the number of elements of type Type that could be allocated by an object of class allocator before the free memory is used up.  
  35. size_type max_size( ) const;   
  36.   
  37. // A structure that enables an allocator for objects of one type to allocate storage for objects of another type.  
  38. template<class _Other>  
  39. struct rebind {  
  40.    typedef allocator<_Other> other;  
  41. };  
  42.   
  43. <strong>Operators overload</strong>  
  44. template<class Other>  
  45. allocator<Type>& operator=(const allocator<Other>& _Right);  // Assignment operator  
  46.   
  47.    


SGI STL使用的空间配置器

SGI STL底层没有采用上述STL标准规范中的声明,它的配置器与众不同,其名称是alloc而非allocator,而且不接受任何参数。
SGI STL中的每种容器都已经指定其缺省的空间配置器alloc,我们在使用时很少需要自行指定配置器。
SGI STL空间配置器的设计哲学:
  • 从堆中申请内存空间
  • 考虑多线程状态
  • 考虑内存不足时的应变措施
  • 考虑过多“小型区块”可能造成的内存碎片问题
SGI STL空间配置器alloc的设计描述:
  • 使用双层级配置器
  • 第一级配置器(__malloc_alloc_template)直接使用malloc() 和 free() 
  • 第二级配置器(__default_alloc_template)视情况采用不同策略:当配置区块超过128byte时,视之为“足够大”,便调用第一级配置器;否则,视之为“过小”,为了提高效率(1.减少内存碎片;2.降低额外负担:直接使用malloc()申请内存时,系统会要多分配一些内存(内存头)用来管理所分配的内存空间),采用复杂的内存池(memory pool)技术(见下文详解),不再求助于第一级配置器
  • SGI STL为了使配置器的接口能够符合STL的标准规范,提供了一个包装接口simple_alloc:
[cpp]  view plain copy
  1. template<class T, class Alloc>  
  2. class simple_alloc {  
  3. public:  
  4.     static T *allocate(size_t n)  
  5.                 { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); }  
  6.     static T *allocate(void)  
  7.                 { return (T*) Alloc::allocate(sizeof (T)); }  
  8.     static void deallocate(T *p, size_t n)  
  9.                 { if (0 != n) Alloc::deallocate(p, n * sizeof (T)); }  
  10.     static void deallocate(T *p)  
  11.                 { Alloc::deallocate(p, sizeof (T)); }  
  12. };  

第一级配置器__malloc_alloc_template源码剖析

[cpp]  view plain copy
  1. <span style="font-size:10px;">// 基于malloc()的配置器.通常比第二级配置器(__default_alloc_template)慢.  
  2. // 通常是线程安全的,并且对存储空间的使用更加高效.  
  3. #ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG  
  4. # ifdef __DECLARE_GLOBALS_HERE  
  5. void (* __malloc_alloc_oom_handler)() = 0;     // 内存不足处理函数指针  
  6. // g++ 2.7.2 does not handle static template data members.  
  7. # else  
  8. extern void (* __malloc_alloc_oom_handler)();  
  9. # endif  
  10. #endif  
  11.   
  12. template <int inst>  // 没有模版型别参数,至于非型别参数inst没有用到  
  13. class __malloc_alloc_template {  
  14. private:   // 用来处理内存不足的情况  
  15.     static void *oom_malloc(size_t);  
  16.     static void *oom_realloc(void *, size_t);  
  17. #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG  
  18.     static void (* __malloc_alloc_oom_handler)();  
  19. #endif  
  20.   
  21. public:  
  22.   
  23.     static void * allocate(size_t n)  
  24.     {  
  25.         void *result = malloc(n);    // 直接调用malloc()  
  26.         if (0 == result)   
  27.             result = oom_malloc(n);  // 内存申请失败,调用内存不足处理函数   
  28.         return result;  
  29.     }  
  30.   
  31.     static void deallocate(void *p, size_t /* n */)  
  32.     {  
  33.         free(p);  // 直接调用free()  
  34.     }  
  35.   
  36.     static void * reallocate(void *p, size_t /* old_sz */size_t new_sz)  
  37.     {  
  38.         void * result = realloc(p, new_sz);  // 直接使用realloc()  
  39.         if (0 == result)   
  40.             result = oom_realloc(p, new_sz); // 内存申请失败,调用内存不足处理函数   
  41.         return result;  
  42.     }  
  43.   
  44.     // 指定内存不足处理函数句柄  
  45.     static void (* set_malloc_handler(void (*f)()))()  
  46.     {  
  47.         void (* old)() = __malloc_alloc_oom_handler;  
  48.         __malloc_alloc_oom_handler = f;  
  49.         return(old);  
  50.     }  
  51.   
  52. };  
  53.   
  54. #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG  
  55. template <int inst>  
  56. void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0; // 初值为0,需client设定  
  57. #endif  
  58.   
  59. // 内存不足处理函数:malloc()申请内存失败  
  60. template <int inst>  
  61. void * __malloc_alloc_template<inst>::oom_malloc(size_t n)  
  62. {  
  63.     void (* my_malloc_handler)();  
  64.     void *result;  
  65.   
  66.     for (;;) {  // 不断尝试  
  67.         my_malloc_handler = __malloc_alloc_oom_handler;  
  68.         if (0 == my_malloc_handler) {   
  69.             __THROW_BAD_ALLOC; } // client未设定处理函数,直接抛出异常    
  70.         (*my_malloc_handler)();  // 调用内存不足处理函数  
  71.         result = malloc(n);      // 再次尝试申请内存  
  72.         if (result)   
  73.             return(result);      // 申请成功  
  74.     }  
  75. }  
  76.   
  77. // 内存不足处理函数:realloc()申请内存失败(与oom_malloc()类似)  
  78. template <int inst>  
  79. void * __malloc_alloc_template<inst>::oom_realloc(void *p, size_t n)  
  80. {  
  81.     void (* my_malloc_handler)();  
  82.     void *result;  
  83.   
  84.     for (;;) {  
  85.         my_malloc_handler = __malloc_alloc_oom_handler;  
  86.         if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }  
  87.         (*my_malloc_handler)();  
  88.         result = realloc(p, n);  
  89.         if (result) return(result);  
  90.     }  
  91. }  
  92.   
  93. typedef __malloc_alloc_template<0> malloc_alloc; // inst直接被指定为0</span>  
总结:

  • 第一层配置器比较简单,直接调用相应的C函数
  • 并实现出类似C++ new-handler机制,来处理内存不足的情况

第二级配置器__default_alloc_template源码剖析

[cpp]  view plain copy
  1. // 多线程搞不懂,故去掉了线程相关代码,留待以后分析  
  2. template <bool threads, int inst>  
  3. class __default_alloc_template {  
  4.   
  5. private:  
  6.     // Really we should use static const int x = N  
  7.     // instead of enum { x = N }, but few compilers accept the former.  
  8.     // 唉,为了兼容性,考虑得太周到了  
  9. # ifndef __SUNPRO_CC  
  10.     enum {__ALIGN = 8};  
  11.     enum {__MAX_BYTES = 128};  
  12.     enum {__NFREELISTS = __MAX_BYTES/__ALIGN};  
  13. # endif  
  14.     // 内存对齐:将bytes上调至8的倍数  
  15.     static size_t ROUND_UP(size_t bytes) {  
  16.         return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));  
  17.     }  
  18. __PRIVATE:  
  19.     // free_list的节点构造  
  20.     union obj {  
  21.         union obj * free_list_link;  
  22.         char client_data[1];    /* The client sees this. 这里没搞懂        */    
  23.     };  
  24. private:  
  25. # ifdef __SUNPRO_CC  
  26.     static obj * __VOLATILE free_list[];   
  27.     // Specifying a size results in duplicate def for 4.1  
  28. # else  
  29.     static obj * __VOLATILE free_list[__NFREELISTS]; // free_list数组,16个元素  
  30. # endif  
  31.     // 根据bytes大小获取free_list的数组下标,从0开始  
  32.     static  size_t FREELIST_INDEX(size_t bytes) {  
  33.         return (((bytes) + __ALIGN-1)/__ALIGN - 1);  
  34.     }  
  35.   
  36.     // Returns an object of size n, and optionally adds to size n free list.  
  37.     // 返回一个大小为n的对象,并可能加入大小为n的其它区块到free list中  
  38.     static void *refill(size_t n);  
  39.   
  40.     // Allocates a chunk for nobjs of size "size".  nobjs may be reduced  
  41.     // if it is inconvenient to allocate the requested number.  
  42.     // 配置一块空间,可容纳nobjs个大小为"size"的区块  
  43.     // 如果不能配置nobjs个区块,nobjs的大小可能会减少(按引用传递的)  
  44.     static char *chunk_alloc(size_t size, int &nobjs);  
  45.   
  46.     // 内存池状态  
  47.     static char *start_free;  // 内存池起始位置。只在chunk_alloc()中变化  
  48.     static char *end_free;    // 内存池结束位置。只在chunk_alloc()中变化  
  49.     static size_t heap_size;  // 从堆中申请的内存大小  
  50.   
  51. public:  
  52.   
  53.     /* n must be > 0      */  
  54.     static void * allocate(size_t n)  
  55.     {  
  56.         obj * __VOLATILE * my_free_list;  
  57.         obj * __RESTRICT result;  
  58.   
  59.         if (n > (size_t) __MAX_BYTES) {    
  60.             return(malloc_alloc::allocate(n)); // n大于128byte,直接调用第一级配置器  
  61.         }  
  62.         my_free_list = free_list + FREELIST_INDEX(n); // 从16个free_list中获取合适的一个  
  63.         result = *my_free_list;  
  64.         if (result == 0) {  
  65.             void *r = refill(ROUND_UP(n)); // 没有可用的free_list,重新填充free_list  
  66.             return r;  
  67.         }  
  68.         // 调整free_list:将已使用的内存区块从free_list中移除  
  69.         *my_free_list = result -> free_list_link;   
  70.         return (result);  
  71.     };  
  72.   
  73.     /* p may not be 0 */  
  74.     static void deallocate(void *p, size_t n)  
  75.     {  
  76.         obj *q = (obj *)p;  
  77.         obj * __VOLATILE * my_free_list;  
  78.   
  79.         if (n > (size_t) __MAX_BYTES) {  
  80.             malloc_alloc::deallocate(p, n); // n大于128byte,直接调用第一级配置器  
  81.             return;  
  82.         }  
  83.         my_free_list = free_list + FREELIST_INDEX(n); // 从16个free_list中获取合适的一个  
  84.         // 调整free_list: 将回收内存区块重新添加到free_list中  
  85.         q -> free_list_link = *my_free_list;  
  86.         *my_free_list = q;  
  87.     }  
  88.   
  89.     static void * reallocate(void *p, size_t old_sz, size_t new_sz);  
  90.   
  91. } ;  
  92.   
  93. // static数据的定义与初值设定  
  94. template <bool threads, int inst>  
  95. char *__default_alloc_template<threads, inst>::start_free = 0;  
  96.   
  97. template <bool threads, int inst>  
  98. char *__default_alloc_template<threads, inst>::end_free = 0;  
  99.   
  100. template <bool threads, int inst>  
  101. size_t __default_alloc_template<threads, inst>::heap_size = 0;  
  102.   
  103. template <bool threads, int inst>  
  104. __default_alloc_template<threads, inst>::obj * __VOLATILE  
  105.     __default_alloc_template<threads, inst> ::free_list[  
  106. # ifdef __SUNPRO_CC  
  107.         __NFREELISTS  
  108. # else  
  109.         __default_alloc_template<threads, inst>::__NFREELISTS  
  110. # endif  
  111.     ] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };  
  112. // The 16 zeros are necessary to make version 4.1 of the SunPro  
  113. // compiler happy.  Otherwise it appears to allocate too little  
  114. // space for the array.  
  115.   
  116.   
  117.   
  118. /* We allocate memory in large chunks in order to avoid fragmenting     */  
  119. /* the malloc heap too much.                                            */  
  120. /* We assume that size is properly aligned.                             */  
  121. /* We hold the allocation lock.                                         */  
  122. // 为了避免堆中有过多的内存碎片,我们每次申请一大块内存空间  
  123. // 我们假定这个空间大小是内存对齐的  
  124. template <bool threads, int inst>  
  125. char*  
  126.     __default_alloc_template<threads, inst>::chunk_alloc(size_t size, int& nobjs)  
  127. {  
  128.     char * result;  
  129.     size_t total_bytes = size * nobjs;  
  130.     size_t bytes_left = end_free - start_free;  // 内存池剩余空间大小  
  131.   
  132.     if (bytes_left >= total_bytes) {   // 内存池剩余空间完全满足需求量  
  133.         result = start_free;  
  134.         start_free += total_bytes;  
  135.         return(result);  
  136.     } else if (bytes_left >= size) {   // 内存池剩余空间满足至少一个区块  
  137.         nobjs = bytes_left/size;       // 调整nobjs的值  
  138.         total_bytes = size * nobjs;  
  139.         result = start_free;  
  140.         start_free += total_bytes;  
  141.         return(result);  
  142.     } else {                           // 内存池剩余空间连一个区块都满足不了  
  143.         // 计算需从堆中申请的内存块大小  
  144.         size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);  
  145.   
  146.         // 先把内存池剩余空间分配给合适的free_list  
  147.         if (bytes_left > 0) {    
  148.             obj * __VOLATILE * my_free_list =  
  149.                 free_list + FREELIST_INDEX(bytes_left);  
  150.   
  151.             ((obj *)start_free) -> free_list_link = *my_free_list;  
  152.             *my_free_list = (obj *)start_free;  
  153.         }  
  154.   
  155.         start_free = (char *)malloc(bytes_to_get); // 从堆中申请内存,补充内存池  
  156.         if (0 == start_free) {                     // 申请内存失败  
  157.             int i;  
  158.             obj * __VOLATILE * my_free_list, *p;  
  159.             // Try to make do with what we have.  That can't  
  160.             // hurt.  We do not try smaller requests, since that tends  
  161.             // to result in disaster on multi-process machines.  
  162.             // 先尝试检查我们手上(free_list)拥有的东西。这不会造成伤害。  
  163.             // 我们不打算检查比size更小的区块,因为那在多进程机器上容易导致灾难。  
  164.             for (i = size; i <= __MAX_BYTES; i += __ALIGN) {  
  165.                 my_free_list = free_list + FREELIST_INDEX(i);  
  166.                 p = *my_free_list;  
  167.                 if (0 != p) {   // 存在未用区块  
  168.                     // 释放该未用区块  
  169.                     *my_free_list = p -> free_list_link;   
  170.                     start_free = (char *)p;  
  171.                     end_free = start_free + i;  
  172.                     return(chunk_alloc(size, nobjs)); // 递归调用自己,重新分配  
  173.                     // Any leftover piece will eventually make it to the  
  174.                     // right free list.  
  175.                     // 任何内存池剩余空间终将被编入适当的free list  
  176.                 }  
  177.             }  
  178.   
  179.             // 55,山穷水尽了  
  180.             end_free = 0;     
  181.             // 调用第一级配置器,看看内存不足处理函数能不能起作用  
  182.             start_free = (char *)malloc_alloc::allocate(bytes_to_get);  
  183.             // This should either throw an  
  184.             // exception or remedy the situation.  Thus we assume it  
  185.             // succeeded.  
  186.             // 要么抛出异常,要么内存不足的情况得以改善,我们假定它成功了  
  187.         }  
  188.         heap_size += bytes_to_get;  
  189.         end_free = start_free + bytes_to_get;  
  190.         return(chunk_alloc(size, nobjs));  // 递归调用自己,重新分配  
  191.     }  
  192. }  
  193.   
  194.   
  195. /* Returns an object of size n, and optionally adds to size n free list.*/  
  196. /* We assume that n is properly aligned.                                */  
  197. /* We hold the allocation lock.                                         */  
  198. // 返回一个大小为n的指针对象,并且有可能会为适当的free list增加节点  
  199. // 假定n已经适当调整到8的倍数了  
  200. template <bool threads, int inst>  
  201. void* __default_alloc_template<threads, inst>::refill(size_t n)  
  202. {  
  203.     int nobjs = 20;  // 缺省取得20个新节点  
  204.     char * chunk = chunk_alloc(n, nobjs);  
  205.     obj * __VOLATILE * my_free_list;  
  206.     obj * result;  
  207.     obj * current_obj, * next_obj;  
  208.     int i;  
  209.   
  210.     if (1 == nobjs)   
  211.         return(chunk);  // 只获得一个节点,直接返回  
  212.     // 否则准备调整free list,纳入新节点  
  213.     my_free_list = free_list + FREELIST_INDEX(n); // 找到合适的free list  
  214.   
  215.     /* Build free list in chunk */  
  216.     // 纳入新节点  
  217.     result = (obj *)chunk;  
  218.     *my_free_list = next_obj = (obj *)(chunk + n);   
  219.     for (i = 1; ; i++) {  
  220.         current_obj = next_obj;  
  221.         next_obj = (obj *)((char *)next_obj + n);  
  222.         if (nobjs - 1 == i) {  
  223.             current_obj -> free_list_link = 0;  
  224.             break;  
  225.         } else {  
  226.             current_obj -> free_list_link = next_obj;  
  227.         }  
  228.     }  
  229.     return(result);  
  230. }  
  231.   
  232. template <bool threads, int inst>  
  233. void*  
  234.     __default_alloc_template<threads, inst>::reallocate(void *p,  
  235.     size_t old_sz,  
  236.     size_t new_sz)  
  237. {  
  238.     void * result;  
  239.     size_t copy_sz;  
  240.   
  241.     if (old_sz > (size_t) __MAX_BYTES && new_sz > (size_t) __MAX_BYTES) {  
  242.         return(realloc(p, new_sz));  // 疑问:这里怎么不直接调用第一级配置器里面的realloc  
  243.     }  
  244.     if (ROUND_UP(old_sz) == ROUND_UP(new_sz)) return(p);  
  245.     result = allocate(new_sz);  
  246.     copy_sz = new_sz > old_sz? old_sz : new_sz;  
  247.     memcpy(result, p, copy_sz);  
  248.     deallocate(p, old_sz);  
  249.     return(result);  
  250. }  
  251.   
  252. typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;  
  253. typedef __default_alloc_template<false, 0> single_client_alloc;  
总结:
  • 第二层配置器的核心就是内存池
  • SGI STL的内存池通过free_list数组来管理,数组中包含16个free list,每个free list节点大小依次从8、16、24递增到128byte
  • 分配内存时(假定分配大小在128byte内),首先根据分配大小找到合适的free list,如果该free list中没有可用的节点,则调用refill()函数从内存池中取空间给该free list用,然后返回第一个节点的内存空间,并调整该free list
  • refill()缺省取20个新节点(区块),通过调用chunk_alloc(),然后根据取得新节点的实际个数,调整相应的free list(大于1)
  • chunk_alloc()内部实现最为复杂,根据内存池剩余空间大小与需求量的关系,分为三种情况,具体看代码,不再赘述

1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值