STL简介

  STL(Standard Template Library,标准模板库),从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其他一些组件的集合。

谈及组件,那么我们就首先来简单谈下STL六大组件,其相关的设计模式使用,以及各组件之间的协作关系。

设计模式一览

六大组件简单介绍

1. 空间配置器:内存池实现小块内存分配,对应到设计模式--单例模式(工具类,提供服务,一个程序只需要一个空间配置器即可),享元模式(小块内存统一由内存池进行管理)

2.迭代器:迭代器模式,模板方法

3.容器:STL的核心之一,其他组件围绕容器进行工作:迭代器提供访问方式,空间配置器提供容器内存分配,算法对容器中数据进行处理,仿函数伪算法提供具体的策略,类型萃取  实现对自定义类型内部类型提取。保证算法覆盖性。其中涉及到的设计模式:组合模式(树形结构),门面模式(外部接口提供),适配器模式(stack,queue通过deque适配得  到),建造者模式(不同类型树的建立过程)。

4.类型萃取:基于范型编程的内部类型解析,通过typename获取。可以获取迭代器内部类型value_type,Poter,Reference等。

5.仿函数:一种类似于函数指针的可回调机制,用于算法中的决策处理。涉及:策略模式,模板方法。

6适配器:STL中的stack,queue通过双端队列deque适配实现,map,set通过RB-Tree适配实现。涉及适配器模式。

关于六大组件之间的具体关系如图简单描述

ps(图技术比较水,见谅,如有bug,请指正)

貌似扯的多了,来谈谈主题《空间配置器》问题吧。

STL空间配置器产生的缘由:

  在软件开发,程序设计中,我们不免因为程序需求,使用很多的小块内存(基本类型以及小内存的自定义类型)。在程序中动态申请,释放。

这个过程过程并不是一定能够控制好的,于是乎,

问题1:就出现了内存碎片问题。(ps外碎片问题)

问题2:一直在因为小块内存而进行内存申请,调用malloc,系统调用产生性能问题。

注:内碎片:因为内存对齐/访问效率(CPU取址次数)而产生 如 用户需要3字节,实际得到4或者8字节的问题,其中的碎片是浪费掉的。

  外碎片:系统中内存总量足够,但是不连续,所以无法分配给用户使用而产生的浪费。下边简单图解

这两个问题解释清楚之后,就来谈STL空间配置器的实现细节了

实现策略

  用户申请空间大于128?

  yes:调用一级空间配置器

  no:调用二级空间配置器

大致实现为:

  二级空间配置由内存池以及伙伴系统:自由链表组成

  一级空间配置器直接封装malloc,free进行处理,增加了C++中的set_handler机制(这里其实也就是个略显牵强的装饰/适配模式了),增加内存分配时客户端可选处理机制。

可配置性:

  客户端可以通过宏__USE_MALLOC进行自定义选择是否使用二级空间配置器。

一级空间配置器就主要封装malloc,添加handler机制了,这里就不罗嗦了,相信各位都是可以通过源码了解到的

关于二级空间配置器:

最后再罗嗦一点,说说Trace问题,然后就给出代码了。

Trace使用

  对于内存池的内部实现过程共还是比较复杂的,虽然代码量,函数比较简单。但是调用过程可能比较复杂。这时,如果我们选择debug调试,过程会相当的繁琐,需要仔细记录调用堆栈过程以及数据流向,逻辑变更等。对于楼主这种水货来说,估计完事就要苦了。

  所以,就使用Trace进行跟踪,打印数据流向,逻辑走向,文件,函数,方法,行位置。那么我们就能根据这个记录进行程序的排错以及调优了。

具体Trace简单如下

  1. #pragma once
  2.  
  3. #define ___TRACE(...) fprintf(fout, "file[%s]line[%u]func[%s]::",__FILE__,__LINE__,__func__);\
  4. fprintf(fout,__VA_ARGS__)

没错,就是这么简单,利用宏打印文件,行,函数位置,然后利用可变参数列表方式接收代码中具体位置的记录跟踪。

如下是代码摘取的Alloc中的跟中。

  1. static void *Allocate(size_t n)
  2. {
  3. ___TRACE("__MallocAllocTemplate to get n = %u\n",n);
  4. void *result = malloc(n);
  5. if ( == result)
  6. {
  7. result = OomMalloc(n);
  8. }
  9. return result;
  10. }

我的天:组织不好,手速太差,终于前奏完成。那么就给出空间配置器的代码了。

具体也可以在个人的github中获取源码

https://github.com/langya0/llhProjectFile/tree/master/STL

文件:Alloc.h

  1. #pragma once
  2.  
  3. #include "Config.h"
  4. __STLBEGIN
  5. #include <memory.h>
  6. #include <stdlib.h>
  7.  
  8. #define __THROW_BAD_ALLOC throw std::bad_alloc()
  9.  
  10. class __MallocAllocTemplate
  11. {
  12. private:
  13. static void *OomMalloc(size_t);
  14. static void *OomRealloc(void *, size_t);
  15. static void(*__malloc_alloc_oom_handler)();
  16. public:
  17. static void *Allocate(size_t n)
  18. {
  19. ___TRACE("__MallocAllocTemplate to get n = %u\n",n);
  20. void *result = malloc(n);
  21. if ( == result)
  22. {
  23. result = OomMalloc(n);
  24. }
  25. return result;
  26. }
  27. static void *Reallocate(void *p, size_t old_sz, size_t new_sz)
  28. {
  29. void* result = realloc(p, new_sz);
  30. if ( == result)
  31. {
  32. result = OomRealloc(p, new_sz);
  33. return result;
  34. }
  35. }
  36.  
  37. static void Deallocate(void *p,size_t n)
  38. {
  39. ___TRACE("一级空间配置器释放 p= %p n = %u\n",p,n);
  40. free(p);
  41. }
  42.  
  43. //
  44. static void(*set_malloc_handler(void(*f)()))()
  45. {
  46. ___TRACE("一级空间配置器,set Handler f = %p\n",f);
  47. void(*old)() = __malloc_alloc_oom_handler;
  48. __malloc_alloc_oom_handler = f;
  49. return (old);
  50. }
  51.  
  52. };
  53.  
  54. void *__MallocAllocTemplate::OomMalloc(size_t n)
  55. {
  56. ___TRACE("一级空间配置器,不足进入Oo中n = %u\n",n);
  57. void(*my_malloc_handler)();
  58. void* result;
  59. for (;;)
  60. {
  61. my_malloc_handler = __malloc_alloc_oom_handler;
  62. if ( == my_malloc_handler)
  63. {
  64. __THROW_BAD_ALLOC;
  65. }
  66. (*__malloc_alloc_oom_handler)();
  67. result = malloc(n);
  68. if (result)
  69. return result;
  70. }
  71. }
  72.  
  73. void* __MallocAllocTemplate::OomRealloc(void* p, size_t n)
  74. {
  75. void(*my_malloc_handler)();
  76. void* result;
  77.  
  78. for (;;)
  79. {
  80. my_malloc_handler = __malloc_alloc_oom_handler;
  81. if ( == my_malloc_handler)
  82. {
  83. __THROW_BAD_ALLOC;
  84. }
  85. (*my_malloc_handler)();
  86. result = realloc(p, n);
  87. if (result)
  88. return result;
  89. }
  90. }
  91. void(*__MallocAllocTemplate::__malloc_alloc_oom_handler)() = ;
  92.  
  93. typedef __MallocAllocTemplate MallocAlloc; //////这里放在#ifdef前边是因为二级空间配置器中还需要调用一级空间配置器
  94. /////////////////////////////////////////根据是否配置__USE_ALLOC选择使用一级还是二级空间配置器
  95. #ifdef __USE_ALLOC
  96. typedef MallocAlloc Alloc;
  97. #else //not define __USE_ALLOC
  98.  
  99. template <bool threads, int inst>
  100. class __DefaultAllocTemplate
  101. {
  102. protected:
  103. enum {_ALIGN = };
  104. enum {_MAX_BYTES = };
  105. enum {_NFREELISTS = }; // _MAX_BYTES/_ALIGN
  106.  
  107. static size_t RoundUp(size_t bytes)
  108. {
  109. return (((bytes) + (size_t) _ALIGN-) & ~((size_t) _ALIGN - ));
  110. }
  111.  
  112. protected:
  113. union _Obj {
  114. // 节点链接指针
  115. union _Obj* _freeListLink;
  116. //客户端数据
  117. char _ClientData[];
  118. };
  119.  
  120. //桶结构,保存链表
  121. static _Obj* _freeList[_NFREELISTS];
  122.  
  123. //获取具体大小元素在表中的下标
  124. static size_t _FreeListIndex(size_t __bytes)
  125. {
  126. return (((__bytes) + (size_t)_ALIGN-)/(size_t)_ALIGN - );
  127. }
  128.  
  129. static char* _start_free;
  130. static char* _end_free;
  131. static size_t _heap_size;
  132. public:
  133. static void* Allocate(size_t n)
  134. {
  135. void * ret = ;
  136. ___TRACE("二级空间配置器申请n = %u\n",n);
  137. if(n>_MAX_BYTES)
  138. ret = MallocAlloc::Allocate(n);
  139.  
  140. _Obj* volatile * __my_free_list = _freeList + _FreeListIndex(n);
  141.  
  142. _Obj* __result = *__my_free_list;
  143. if (__result == )
  144. ret = _Refill(RoundUp(n));
  145. else
  146. {
  147. *__my_free_list = __result -> _freeListLink;
  148. ret = __result;
  149. }
  150. return ret;
  151. }
  152.  
  153. static void Deallocate(void* p, size_t n)
  154. {
  155. ___TRACE("二级空间配置器删除p = %p,n = %d\n",p,n);
  156. if (n > (size_t) _MAX_BYTES)
  157. MallocAlloc::Deallocate(p, n);
  158. else
  159. {
  160. _Obj* volatile* __my_free_list = _freeList + _FreeListIndex(n);
  161. _Obj* q = (_Obj*)p;
  162. q -> _freeListLink = *__my_free_list;
  163. *__my_free_list = q;
  164. }
  165. }
  166.  
  167. static void *Reallocate(void* p,size_t __old_sz,size_t __new_sz)
  168. {
  169. ___TRACE("二级空间配置器重新申请p = %p,new_sz = %d\n",p,__new_sz);
  170. void* __result;
  171. size_t __copy_sz;
  172.  
  173. if (__old_sz > (size_t)_MAX_BYTES && __new_sz > (size_t)_MAX_BYTES)
  174. {
  175. return(realloc(p, __new_sz));
  176. }
  177.  
  178. if (RoundUp(__old_sz) == RoundUp(__new_sz))
  179. return(p);
  180.  
  181. __result = Allocate(__new_sz);
  182.  
  183. __copy_sz = __new_sz > __old_sz? __old_sz : __new_sz;
  184. memcpy(__result, p, __copy_sz);
  185. Deallocate(p, __old_sz);
  186. return(__result);
  187. }
  188. protected:
  189. static void *_Refill(size_t n)
  190. {
  191. ___TRACE("二级空间配置器自由链表填充n = %u\n",n);
  192.  
  193. size_t nobjs = ;
  194. void * ret;
  195. char * chunks = _ChunkAlloc(n,nobjs);
  196.  
  197. if(nobjs == )
  198. {
  199. return chunks;
  200. }
  201. _Obj* volatile * __my_free_list = _freeList+_FreeListIndex(n);
  202. ret = chunks;
  203.  
  204. for(size_t i = ;i<nobjs;i++)
  205. {
  206. ((_Obj*)(chunks+n*i))->_freeListLink = *__my_free_list;
  207. *__my_free_list = (_Obj*)(chunks+n*i);
  208. }
  209. return ret;
  210. }
  211.  
  212. /////////////这里的nobjs使用&,,内部需要复用逻辑,可能改变之
  213. static char * _ChunkAlloc(size_t n,size_t &nobjs)
  214. {
  215. size_t totalBytes = n*nobjs;
  216. size_t bytesLeft = _end_free - _start_free;
  217.  
  218. if(bytesLeft>=totalBytes)
  219. {
  220. ___TRACE("二级空间配置器内存池填充n = %u,nobjs = %d\n",n,nobjs);
  221. _start_free += n*nobjs;
  222. return _start_free;
  223. }
  224. else if(bytesLeft>=n)
  225. {
  226. nobjs = (_end_free- _start_free)/n;
  227. ___TRACE("二级空间配置器内存池填充n = %u,nobjs = %d\n",n,nobjs);
  228. _start_free +=n*nobjs;
  229. return _start_free;
  230. }
  231.  
  232. //bytesLeft [0,1)
  233. _Obj* volatile * __my_free_list = _freeList + _FreeListIndex(bytesLeft);
  234. if(_start_free)
  235. {
  236. ___TRACE("二级空间配置器剩余bytesLeft = %u\n",bytesLeft);
  237. ((_Obj*)_start_free)->_freeListLink=*__my_free_list;
  238. *__my_free_list = (_Obj*)_start_free;
  239. }
  240.  
  241. size_t bytesToGet = nobjs*n*+(_heap_size>>);
  242.  
  243. //malloc
  244. _start_free = (char*)malloc(bytesToGet);
  245.  
  246. if(!_start_free)
  247. {
  248. ___TRACE("二级空间配置器malloc失败,在后续链表查找n = %d\n",n);
  249. for(size_t i = n + _ALIGN;i < _MAX_BYTES;i+=_ALIGN)
  250. {
  251. _Obj* volatile * cur = _freeList+_FreeListIndex(i);
  252. if(*cur)
  253. {
  254. *cur = (*cur)->_freeListLink;
  255. _start_free = (char*)*cur;
  256. _end_free = _start_free + i;
  257.  
  258. return _ChunkAlloc(n,nobjs);
  259. }
  260. }
  261.  
  262. ___TRACE("二级空间配置器:后续链表查找失败,转接一级配置,借用handler机制终止程序或者得到空间n = %d\n",n);
  263. _start_free = (char*)MallocAlloc::Allocate(n);
  264. return _ChunkAlloc(n,nobjs);
  265. }
  266. else
  267. {
  268. _end_free = _start_free + bytesToGet;
  269. _heap_size += bytesToGet;
  270. return _ChunkAlloc(n,nobjs);
  271. }
  272.  
  273. }
  274. };
  275.  
  276. template <bool __threads, int __inst>
  277. char* __DefaultAllocTemplate<__threads, __inst>::_start_free= ;
  278.  
  279. template <bool __threads, int __inst>
  280. char* __DefaultAllocTemplate<__threads, __inst>::_end_free = ;
  281.  
  282. template <bool __threads, int __inst>
  283. size_t __DefaultAllocTemplate<__threads, __inst>::_heap_size = ;
  284.  
  285. // static _Obj* _freeList[_NFREELISTS];
  286. template <bool threads, int inst>
  287. typename __DefaultAllocTemplate<threads,inst>::_Obj*
  288. __DefaultAllocTemplate<threads,inst>::_freeList[__DefaultAllocTemplate<threads,inst>::_NFREELISTS]
  289. = {, , , , , , , , , , , , , , , };
  290.  
  291. typedef __DefaultAllocTemplate<,> Alloc;
  292. #endif
  293.  
  294. __STLEND
  295.  
  296. void handler()
  297. {
  298. cout << "here in handler!\n"<<endl;
  299. }
  300. void test()
  301. {
  302. // stl::Alloc::set_malloc_handler(handler);
  303. void *arr[] = {};
  304. ___TRACE("Clint to Get size = %u\n",);
  305.  
  306. // for(size_t i =0;i < 21;++i)
  307. // arr[i] = stl::Alloc::Allocate(5);
  308. // for(size_t i =0;i < 21;++i)
  309. // stl::Alloc::Deallocate(arr[i],5);
  310. arr[] = stl::Alloc::Allocate();
  311. arr[] = stl::Alloc::Allocate();
  312.  
  313. arr[] = stl::Alloc::Allocate();
  314. }

文件:Trace.h

  1. #pragma once
  2.  
  3. #define ___TRACE(...) fprintf(fout, "file[%s]line[%u]func[%s]::",__FILE__,__LINE__,__func__);\
  4. fprintf(fout,__VA_ARGS__)

文件Config.h

  1. #pragma once
  2.  
  3. /////模拟std实现宏方式开始,结束命名空间
  4. namespace __STLNAMESPACE {}
  5.  
  6. #define __STLBEGIN namespace __STLNAMESPACE {
  7.  
  8. #define __STLEND }
  9.  
  10. namespace stl = __STLNAMESPACE;
  11.  
  12. //for malloc
  13. // #define __USE_MALLOC
  14.  
  15. ///for trace
  16. #define __DEBUG
  17. #ifdef __DEBUG
  18. #include <stdio.h>
  19. #define fout stdout ///方便配置,修改Trace记录位置
  20. #endif

终于大功告成,源码finished,那么,再给出测试结果截图了

最后,完成了主题工作之后。

再来说一些空间配置器的遗留问题吧:

1.仔细探究源码之后,你一定会发现一个问题,

  貌似二级空间配置器中的空间重头到尾都没看到他归还给系统。那么问题就是,内存池空间何时释放?

对于这个问题,在回头浏览一下源码及结构图,你就会发现

  大于128的内存,客户程序Deallocate之后会调free释放掉,归还给了系统。

  但是呢...............

  内存池中获取的空间,最终,假定用户都调用Dealloc释放调了,那么他们又在哪里呢?

    没有还给系统,没有在内存池,在自由链表中。

Got it:程序中不曾释放,只是在自由链表中,且配置器的所有方法,成员都是静态的,那么他们就是存放在静态区。释放时机就是程序结束。

2.如果需要释放,那么应该怎么处理呢?

  因为真正可以在程序运行中就归还系统的只有自由链表中的未使用值,但是他们并不一定是连续的(用户申请空间,释放空间顺序的不可控制性),所以想要在合适时间(eg一级配置器的handler中释放,或者设置各阀值,分配空间量到达时处理),就必须保证释放的空间要是连续的。保证连续的方案就是:跟踪分配释放过程,记录节点信心。释放时,仅释放连续的大块。

3.关于STL空间配置器的效率考究

  既然已经存在,而又被广泛使用,那么,整体的效率,以及和STL内部容器之间的使用配合还是没问题的。

我们考虑几种情况:

  a. 用户只需要无限的char类型空间,然而配置器中却对齐到8,于是乎,整个程序中就会有7/8的空间浪费。

  b.对于假定用户申请N次8空间,将系统资源耗到一定程度,然后全部释放了,自由链表中的空间都是连续的。却没有释放。

    但是:用户需要申请大于8的空间时,却依旧没有空间可用。

总之:这个问题就是,空间可能全部积攒在小块自由链表中,却没有用户可用的。这就尴尬了。

最后,关于配置器的其它问题,如果各位有什么新的思考,欢迎交流。邮箱:15829391774@163.com

STL空间配置器那点事的更多相关文章

  1. stl空间配置器线程安全问题补充

    摘要 在上一篇博客<STL空间配置器那点事>简单介绍了空间配置器的基本实现 两级空间配置器处理,一级相关细节问题,同时简单描述了STL各组件之间的关系以及设计到的设计模式等. 在最后,又关 ...

  2. 【转】STL空间配置器

    STL空间配置器(allocator)在所有容器内部默默工作,负责空间的配置和回收.STL标准为空间配置器定义了标准接口(可见<STL源码剖析>P43).而具体实现细节则由各编译器实现版本 ...

  3. STL空间配置器

    1.什么是空间配置器? 空间配置器负责空间配置与管理.配置器是一个实现了动态空间配置.空间管理.空间释放的class template.以内存池方式实现小块内存管理分配.关于内存池概念可以点击:内存池 ...

  4. 咬碎STL空间配置器

    STL空间配置器 一.开场白: 给我的感觉就是,了解是空间配置器的功能,是那么的明了:在看原理,我还是很开心:接下来是360度大转变: 那么长的变量或者函数命名.那么多的宏.不爽,不过,遇上我这种二货 ...

  5. STL空间配置器解析和实现

    STL空间配置器的强大和借鉴作用不言而喻,查阅资料,发现了Dawn_sf已经对其有了极其深入和详细的描述,所以决定偷下懒借用其内容,只提供自己实现STL空间配置器的源码,具体解析内容参考:(一)STL ...

  6. 【陪你系列】5 千字长文+ 30 张图解 | 陪你手撕 STL 空间配置器源码

    大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub https://github.com/rongweihe/MoreT ...

  7. STL——空间配置器(构造和析构基本工具)

    以STL的运用角度而言,空间配置器是最不需要介绍的东西,它总是隐藏在一切组件(更具体地说是指容器,container)的背后,默默工作,默默付出.但若以STL的实现角度而言,第一个需要介绍的就是空间配 ...

  8. STL空间配置器、vector、list、deque、map复习

    本文写于2017-03-03,从老账号迁移到本账号,原文地址:https://www.cnblogs.com/huangweiyang/p/6440830.html STL的六大组件:容器.算法.迭代 ...

  9. STL——空间配置器(SGI-STL)

    一. 空间配置器标准接口 参见<STL源码剖析>第二章-2.1.<memory>文件. 二.具备次配置力的SGI空间配置器 1. SGI STL的配置器与众不同,也与标准规范不 ...

随机推荐

  1. spring的AOP个人理解和使用

    1什么是AOP:AOP是面向切面编程,也就是说面向某个功能模块编程,典型的应用就是Spring的声明式事务, Spring的AOP事务解析: 在以前的事务管理是要融合在逻辑代码中的,在逻辑代码中决定事 ...

  2. SpringCloud 进阶之Zuul(路由网关)

    1. Zuul(路由网关) Zuul 包含了对请求的路由和过滤两个最主要的功能; 路由功能:负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础; 过滤功能:负责对请求的处理过程进行干 ...

  3. (1.4)DML增强功能-Output

    Output在CRUD的区别 1.对于INSERT,可以引用inserted表以查询新行的属性.在insert into table output . 2.对于DELETE,可以引用deleted表以 ...

  4. A Basic Example of Threads Synchronization in Python, python中的线程同步示例

    http://zulko.github.io/blog/2013/09/19/a-basic-example-of-threads-synchronization-in-python/ We will ...

  5. (24)如何使用HttpClient

    介绍 HttpClient是HTTP客户端的接口.HttpClient封装了各种对象,处理cookies,身份认证,连接管理等. 概念 HttpClient的使用一般包含下面6个步骤: 创建 Http ...

  6. ubuntu-未信任的应用程序启动器-XX-Net.desktop

      在安装启动xxnet时使用sudo命令,该软件打开后提示[未信任的应用程序启动器]如图所示,解决办法简介:(1)更换成root用户(2)更改权限   背景描述 xx-net中的启动程序有权限设置, ...

  7. Mkdocs 搭建

    1. 利用pip安装mkdocs sudo pip install mkdocs 2.如果报pip不存在 或是 报权限错误,要不是pip没有安装,就是python里某个库没有关联上,这时候需要重新安装 ...

  8. Vue学习笔记之Vue的面向对象

    0x00 准备工作 JavaScript 语言中,生成实例对象的传统方法是通过构造函数. function Animal(name,age){ this.name = name; this.age = ...

  9. 一次频繁Full GC问题排查过程分享

    问题描述 应用收到频繁Full GC告警 问题排查 登录到对应机器上去,查看GC日志,发现YGC一分钟已经达到了15次,比Full GC还要频繁一些,其中Full GC平均10分钟超过了4次,如下图 ...

  10. Hash课堂测试补写

    Hash课堂测试补写 测试要求: 利用除留余数法为下列关键字集合的存储设计hash函数,并画出分别用开放寻址法和拉链法解决冲突得到的空间存储状态(散列因子取0.75) 关键字集合:85,75,57,6 ...