摘要

C++STL的空间配置器将内存的分配、释放,对象的构造、析构都分开执行,内存分配由alloc::allocate()负责,内存的释放由alloc::deallocate()负责;对象的构造由::construct()负责,对象的析构由::destroy()负责。

构造和析构:construct()和destroy()

下面为源码和注释:

  1. #include <new.h> //使用placement new
  2. //使用placement new在已经分配的内存上构造对象
  3. template <class T1, class T2>
  4. inline void construct(T1* p, const T2& value) {
  5. new (p) T1(value);
  6. }
  7. //第一个版本,接受一个指针
  8. //调用对象的析构函数,它需要有non-trivial destructor
  9. template <class T>
  10. inline void destroy(T* pointer) {
  11. pointer->~T();
  12. }
  13. //第二个版本
  14. //针对迭代器为char*和wchar_t*的特化版本
  15. inline void destroy(char*, char*) {}
  16. inline void destroy(wchar_t*, wchar_t*) {}
  17. //接受两个迭代器,找出元素的类型
  18. template <class ForwardIterator>
  19. inline void destroy(ForwardIterator first, ForwardIterator last) {
  20. __destroy(first, last, value_type(first));
  21. }
  22. //判断元素的类型是否有trivial destructor
  23. template <class ForwardIterator, class T>
  24. inline void __destroy(ForwardIterator first, ForwardIterator last, T*) {
  25. typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
  26. __destroy_aux(first, last, trivial_destructor());
  27. }
  28. //元素的类型有non-trivial destructor
  29. template <class ForwardIterator>
  30. inline void
  31. __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
  32. for ( ; first < last; ++first)
  33. destroy(&*first);
  34. }
  35. //元素的类型有trivial destructor
  36. template <class ForwardIterator>
  37. inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}

空间的配置和释放: std::alloc

在内存配置方面,STL分为两级配置器,当请求的内存大于128bytes的时候调用第一级配置器,当请求的内存小于等于128bytes的时候调用第二级配置器。如图:

第一级配置器源码:

  1. template <int inst>
  2. class __malloc_alloc_template
  3. {
  4. private:
  5. //调用malloc函数不成功后调用
  6. static void *oom_malloc(size_t);
  7. //调用realloc函数不成功后调用
  8. static void *oom_realloc(void *, size_t);
  9. //类似于C++的set_new_handle错误处理函数一样,如果不设置,在内存不足时,返回THROW_BAD_ALLOC
  10. #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
  11. static void (* __malloc_alloc_oom_handler)();
  12. #endif
  13. public:
  14. //直接调用malloc来分配内存
  15. static void * allocate(size_t n)
  16. {
  17. void *result = malloc(n);
  18. if (0 == result) result = oom_malloc(n); //如果分配失败,则调用oom_malloc()
  19. return result;
  20. }
  21. //第一级配置器直接调用free来释放内存
  22. static void deallocate(void *p, size_t /* n */)
  23. {
  24. free(p);
  25. }
  26. //直接调用reallloc来分配内存
  27. static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz)
  28. {
  29. void * result = realloc(p, new_sz);
  30. if (0 == result) result = oom_realloc(p, new_sz); //如果realloc分配不成功,调用oom_realloc()
  31. return result;
  32. }
  33. //异常处理函数,即内存分配失败后的处理
  34. static void (* set_malloc_handler(void (*f)()))()
  35. {
  36. void (* old)() = __malloc_alloc_oom_handler;
  37. __malloc_alloc_oom_handler = f;
  38. return(old);
  39. }
  40. };
  41. // 以下是针对内存分配失败后的处理
  42. //首先,将__malloc_alloc_oom_handler的默认值设为0
  43. template <int inst>
  44. void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;
  45. #endif
  46. template <int inst>
  47. void * __malloc_alloc_template<inst>::oom_malloc(size_t n)
  48. {
  49. void (* my_malloc_handler)();
  50. void *result;
  51. for (;;) { // 不断地尝试释放、再配置、再释放、再配置
  52. my_malloc_handler = __malloc_alloc_oom_handler;
  53. if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; } //这里是当没有设置处理函数的时候,直接抛出异常
  54. (*my_malloc_handler)(); // 调用处理例程,尝试释放内存
  55. result = malloc(n); // 再重新分配内存
  56. if (result) return(result); // 如果分配成功则返回指针
  57. }
  58. }
  59. template <int inst>
  60. void * __malloc_alloc_template<inst>::oom_realloc(void *p, size_t n)
  61. {
  62. void (* my_malloc_handler)();
  63. void *result;
  64. for (;;) { //不断地尝试释放、再配置、再释放、再配置
  65. my_malloc_handler = __malloc_alloc_oom_handler;
  66. if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; } //这里是当没有设置处理函数的时候,直接抛出异常
  67. (*my_malloc_handler)(); // 调用处理例程,尝试释放内存
  68. result = realloc(p, n); // 再重新分配内存
  69. if (result) return(result); // 如果分配成功则返回指针
  70. }
  71. }

第二级配置器源码:

  1. enum {__ALIGN = 8}; //小型区块的上调边界
  2. enum {__MAX_BYTES = 128}; //小型区块的上限
  3. enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; //free-lists个数
  4. //第一参数用于多线程,这里不做讨论。
  5. template <bool threads, int inst>
  6. class __default_alloc_template
  7. {
  8. private:
  9. // 此函数将bytes的边界上调至8的倍数
  10. static size_t ROUND_UP(size_t bytes)
  11. {
  12. return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));
  13. }
  14. private:
  15. union obj
  16. {
  17. union obj * free_list_link;
  18. char client_data[1];
  19. };
  20. private:
  21. //16个free-lists
  22. static obj * __VOLATILE free_list[__NFREELISTS];
  23. // 根据待待分配的空间大小, 在free_list中选择合适的大小
  24. static size_t FREELIST_INDEX(size_t bytes)
  25. {
  26. return (((bytes) + __ALIGN-1)/__ALIGN - 1);
  27. }
  28. // 返回一个大小为n的对象,并可能加入大小为n的其它区块到free-lists
  29. static void *refill(size_t n);
  30. // 配置一大块空间,可容纳nobjs个大小为“size”的区块
  31. // 如果配置nobjs个区块有所不便,nobjs可能会降低,所以需要用引用传递
  32. static char *chunk_alloc(size_t size, int &nobjs);
  33. // 内存池
  34. static char *start_free; // 内存池起始点,只在chunk_alloc()中变化
  35. static char *end_free; // 内存池结束点,只在chunk_alloc()中变化
  36. static size_t heap_size; // 已经在堆上分配的空间大小
  37. public:
  38. static void* allocate(size_t n);// 空间配置函数
  39. static void deallocate(void *p, size_t n); // 空间释放函数
  40. static void* reallocate(void* p, size_t old_sz , size_t new_sz); //空间重新配置函数
  41. }
  42. // 一些静态成员变量的初始化
  43. // 内存池起始位置
  44. template <bool threads, int inst>
  45. char *__default_alloc_template<threads, inst>::start_free = 0;
  46. // 内存池结束位置
  47. template <bool threads, int inst>
  48. char *__default_alloc_template<threads, inst>::end_free = 0;
  49. // 已经在堆上分配的空间大小
  50. template <bool threads, int inst>
  51. size_t __default_alloc_template<threads, inst>::heap_size = 0;
  52. // 内存池容量索引数组
  53. template <bool threads, int inst>
  54. __default_alloc_template<threads, inst>::obj * __VOLATILE
  55. __default_alloc_template<threads, inst> ::free_list[__NFREELISTS ] =
  56. {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
  57. // The 16 zeros are necessary to make version 4.1 of the SunPro
  58. // compiler happy. Otherwise it appears to allocate too little
  59. // space for the array.
  60. public:
  61. //空间配置函数allocate()
  62. static void * allocate(size_t n)
  63. {
  64. //指向freelist的指针
  65. obj * __VOLATILE * my_free_list;
  66. obj * __RESTRICT result;
  67. //区块size大于128就调用一级配置器,n已被上调至8的倍数
  68. if (n > (size_t) __MAX_BYTES) {
  69. return(malloc_alloc::allocate(n));
  70. }
  71. //寻找freelists中适当的一个
  72. my_free_list = free_list + FREELIST_INDEX(n);
  73. result = *my_free_list;
  74. //没有找到可用的freelist,就重新填充freelist
  75. if (result == 0) {
  76. void *r = refill(ROUND_UP(n));
  77. return r;
  78. }
  79. //调整freelist
  80. *my_free_list = result -> free_list_link;
  81. return (result);
  82. };
  83. //空间释放函数deallocate()
  84. //p不能为nullptr
  85. static void deallocate(void *p, size_t n)
  86. {
  87. obj *q = (obj *)p;
  88. obj * __VOLATILE * my_free_list;
  89. //如果大于128就调用第一级的释放函数
  90. if (n > (size_t) __MAX_BYTES) {
  91. malloc_alloc::deallocate(p, n);
  92. return;
  93. }
  94. //找到对应的freelist
  95. my_free_list = free_list + FREELIST_INDEX(n);
  96. //调整freelist,回收区块
  97. q -> free_list_link = *my_free_list;
  98. *my_free_list = q;
  99. }
  100. } ;
  101. //内存池函数chunk_allco()
  102. template <bool threads, int inst>
  103. char*
  104. __default_alloc_template<threads, inst>::chunk_alloc(size_t size, int& nobjs)
  105. {
  106. char * result;
  107. //所需容量
  108. size_t total_bytes = size * nobjs;
  109. //内存池剩余容量
  110. size_t bytes_left = end_free - start_free;
  111. //如果余量足够
  112. if (bytes_left >= total_bytes) {
  113. result = start_free;
  114. start_free += total_bytes;
  115. return(result);
  116. }
  117. //余量不能完全满足需求,但至少能供应1个区块
  118. else if (bytes_left >= size) {
  119. //计算能供应的最多节点个数
  120. nobjs = bytes_left/size;
  121. total_bytes = size * nobjs;
  122. result = start_free;
  123. //调整内存池开始位置
  124. start_free += total_bytes;
  125. return(result);
  126. } else {
  127. //余量严重不足
  128. size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
  129. // 将剩余内存分配给指定的free_list[FREELIST_INDEX(bytes_left)]
  130. if (bytes_left > 0) {
  131. //内存池还有一些零头
  132. //寻找适当的freelist
  133. obj * __VOLATILE * my_free_list =
  134. free_list + FREELIST_INDEX(bytes_left);
  135. //调整freelist,将内存池中剩余内存编入
  136. ((obj *)start_free) -> free_list_link = *my_free_list;
  137. *my_free_list = (obj *)start_free;
  138. }
  139. //用malloc配置空间,补充内存池
  140. start_free = (char *)malloc(bytes_to_get);
  141. //malloc失败
  142. if (0 == start_free) {
  143. int i;
  144. obj * __VOLATILE * my_free_list, *p;
  145. // Try to make do with what we have. That can't
  146. // hurt. We do not try smaller requests, since that tends
  147. // to result in disaster on multi-process machines.
  148. //不打算配置更小区块,那在多进程机器上会有灾难,无法让其他进程获取内存
  149. //检查freelist中的可用空间,且区块足够大
  150. for (i = size; i <= __MAX_BYTES; i += __ALIGN) {
  151. my_free_list = free_list + FREELIST_INDEX(i);
  152. p = *my_free_list;
  153. if (0 != p) {
  154. //有可用空间,将其加入内存池
  155. *my_free_list = p -> free_list_link;
  156. start_free = (char *)p;
  157. end_free = start_free + i;
  158. //内存池更新完毕,重新分配内存
  159. return(chunk_alloc(size, nobjs));
  160. //任何剩余零头将被编入适当的freelist以备用
  161. // Any leftover piece will eventually make it to the
  162. // right free list.
  163. }
  164. }
  165. //分配失败,就调用一级分配器分配,期待异常处理函数提供帮助
  166. end_free = 0; // In case of exception.
  167. start_free = (char *)malloc_alloc::allocate(bytes_to_get);
  168. // This should either throw an
  169. // exception or remedy the situation. Thus we assume it
  170. // succeeded.
  171. }
  172. heap_size += bytes_to_get;
  173. end_free = start_free + bytes_to_get;
  174. //内存池更新完毕,重新分配内存
  175. return(chunk_alloc(size, nobjs));
  176. }
  177. }
  178. //重新填充freelists
  179. template <bool threads, int inst>
  180. void* __default_alloc_template<threads, inst>::refill(size_t n)
  181. {
  182. //20个区块
  183. int nobjs = 20;
  184. //调用chunk_alloc(),尝试取nobjs个区块作为freelist的新节点
  185. //nobjs为引用传递
  186. char * chunk = chunk_alloc(n, nobjs);
  187. obj * __VOLATILE * my_free_list;
  188. obj * result;
  189. obj * current_obj, * next_obj;
  190. int i;
  191. //若内存池只够分配1个区块,直接返回,freelist无新节点
  192. if (1 == nobjs) return(chunk);
  193. //否则,定位freelist,准备纳入新节点
  194. my_free_list = free_list + FREELIST_INDEX(n);
  195. //在chunk这段空间建立freelist
  196. result = (obj *)chunk;
  197. //略过第一个小区块
  198. *my_free_list = next_obj = (obj *)(chunk + n);
  199. //将freelist的各个节点串接起来
  200. //从第一个开始,第0个返回给客户
  201. for (i = 1; ; i++) {
  202. current_obj = next_obj;
  203. next_obj = (obj *)((char *)next_obj + n);
  204. if (nobjs - 1 == i) {
  205. //尾节点指针域为nullptr
  206. current_obj -> free_list_link = 0;
  207. break;
  208. } else {
  209. current_obj -> free_list_link = next_obj;
  210. }
  211. }
  212. //串接完成并返回
  213. return(result);
  214. }

STL之空间配置器allocator的更多相关文章

  1. C++ STL学习之 空间配置器(allocator)

    众所周知,一般情况下,一个程序包括数据结构和相应的算法,而数据结构作为存储数据的组织形式,与内存空间有着密切的联系. 在C++ STL中,空间配置器便是用来实现内存空间(一般是内存,也可以是硬盘等空间 ...

  2. C++ 空间配置器(allocator)

    C++ 空间配置器(allocator) 在STL中,Memory Allocator 处于最底层的位置,为一切的 Container 提供存储服务,是一切其他组件的基石.对于一般使用 STL 的用户 ...

  3. STL的空间配置器std_alloc 笔记

    STL的空间配置器std_alloc 笔记 C++的内存分配基本操作是 ::operator new(),内存释放是 ::operator delete(),这里个全局函数相当于C的malloc和fr ...

  4. 带你深入理解STL之空间配置器(思维导图+源码)

    前不久把STL细看了一遍,由于看得太"认真",忘了做笔记,归纳和总结这步漏掉了.于是为了加深印象,打算重看一遍,并记录下来里面的一些实现细节.方便以后能较好的复习它. 以前在项目中 ...

  5. 《STL源码剖析》chapter2空间配置器allocator

    为什么不说allocator是内存配置器而说是空间配置器,因为空间不一定是内存,也可以是磁盘或其他辅助介质.是的,你可以写一个allocator,直接向硬盘取空间.sgi stl提供的配置器,配置的对 ...

  6. STL 之 空间配置器(allocator)

    一.SGI 标准的空间配置器,std::allocator SGI也定义了一个符合部分标准,名为allocator的配置器,但是它自己不使用,也不建议我们使用,主要原因是效率不佳. 它只是把C++的操 ...

  7. STL源码剖析 — 空间配置器(allocator)

    前言 以STL的实现角度而言,第一个需要介绍的就是空间配置器,因为整个STL的操作对象都存放在容器之中. 你完全可以实现一个直接向硬件存取空间的allocator. 下面介绍的是SGI STL提供的配 ...

  8. STL学习笔记:空间配置器allocator

    allocator必要接口: allocator::value_type allocator::pointer allocator::const_pointer allocator::referenc ...

  9. STL源码剖析——空间配置器Allocator#1 构造与析构

    以STL的运用角度而言,空间配置器是最不需要介绍的东西,因为它扮演的是幕后的角色,隐藏在一切容器的背后默默工作.但以STL的实现角度而言,最应该首先介绍的就是空间配置器,因为这是这是容器展开一切运作的 ...

随机推荐

  1. Reducetask机制

    Reduce大致分为copy.sort.reduce三个阶段,重点在前两个阶段.copy阶段包含一个eventFetcher来获     取已完成的map列表,由Fetcher线程去copy数据,在此 ...

  2. moya与网络编程思想:网络请求的生命周期

    请求数据管理的集中化: 请求配置的标注化: 请求管理的函数式参量化: 几个端点: target代表应用端的原始数据; endpoint代表应用端到网络端的中间数据,这个数据可以编辑公用数据header ...

  3. NSFileHandle类和NSFileManager,追加数据的操作

    NSFileHandle类主要对文件内容进行读取和写入操作 NSFileManager类主要对文件的操作(删除.修改.移动.复制等等) 常用处理方法 + (id)fileHandleForReadin ...

  4. swift函数式编程之compose

    func a(en:String) -> String { return en + "a"; } func b(en:String) -> String { retur ...

  5. NYOJ104-最大和-(前缀和)

    题意:给一个矩阵,每个元素有正有负,求最大矩阵和. 解题: (1)对原矩阵a用前缀和处理,处理变成矩阵sum,sum[i][j]表示从左上角为a[1][1]到右下角a[i][j]的全部元素和. 矩阵必 ...

  6. 22-1 web传输视频 Opencv+usb摄像头 树莓派+Flask实现视频流媒体WEB服务器

    第一篇 讲解原理 https://blog.miguelgrinberg.com/post/video-streaming-with-flask 第二篇 加入多线程可以直接用 https://gith ...

  7. 08_MSTP(数通华为)

    1. 网络拓扑 2. SW1配置[SW1]vlan batch 10 20 30 40[SW1]stp mode mstp 进入MSTP配置视图,MSTP域名为huawei,同时配置VLAN到实例的映 ...

  8. be of + 名词

    语法: “be of +抽象名词(词组)” 表示主语的某种形状或特征,相当于 "be+形容词" 例如: be of value=be valuable  : be of inter ...

  9. JavaScript var、let、const

    var申明的变量是有作用域的 如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量: 'use strict'; function foo() { var x = 1; ...

  10. GoCN每日新闻(2019-10-28)

    GoCN每日新闻(2019-10-28) 1. 理解和攻击Go DSA验证漏洞 https://paul.querna.org/articles/2019/10/24/dsa-verify-poc/2 ...