STL之空间配置器
2017-07-22 11:39
411 查看
最近在看侯捷老师写的STL源码剖析,想通过看优秀的代码来提高自己的编程水平。
首先STL提供了六大组件,彼此可以套用使用。
借助一张从书上截的图来表明:
Container通过Allocator来获取数据存储空间
Algorithms通过迭代器来获取Container的内容,Functors可以协助Algorithms完成不同策略变化
Adapter可以修饰或者套接Functor由此可见在STL中空间配置器是非常重要的。
简单分析下为什么需要使用空间配置器?
使用空间配置器的原因就是为了解决外碎片
而且我们在使用STL容器的时候,比如vector我们可能需要不断的去申请内存,频繁的申请小块内存效率是非常低的。而STL是非常重视效率的,因此就有了空间配置器来解决这个问题。
STL空间配置器的框架设计
首先设计一级空间配置器
它使用了 __malloc_alloc_template一个模板类来实现一级空间配置器的功能,在这里我可以根据STL给出的简单的模拟实现它,通过这种方式来了解它到底是如何实现的。
上述就是简单的一级空间配置器的设计,当然STL设计的还有realloc在这里我只是简单的模拟实现以下,没有写realloc;
二级空间配置器的设计相较于一级空间配置器的实际就复杂一些
他申请内存并不是直接先问系统索要见下图的过程:
小细节:
首先STL提供了六大组件,彼此可以套用使用。
借助一张从书上截的图来表明:
Container通过Allocator来获取数据存储空间
Algorithms通过迭代器来获取Container的内容,Functors可以协助Algorithms完成不同策略变化
Adapter可以修饰或者套接Functor由此可见在STL中空间配置器是非常重要的。
简单分析下为什么需要使用空间配置器?
使用空间配置器的原因就是为了解决外碎片
而且我们在使用STL容器的时候,比如vector我们可能需要不断的去申请内存,频繁的申请小块内存效率是非常低的。而STL是非常重视效率的,因此就有了空间配置器来解决这个问题。
STL空间配置器的框架设计
首先设计一级空间配置器
它使用了 __malloc_alloc_template一个模板类来实现一级空间配置器的功能,在这里我可以根据STL给出的简单的模拟实现它,通过这种方式来了解它到底是如何实现的。
typedef void(*HANDLE_FUNC)();//HANDLE_FUNC代表一个函数指针 template <int inst> class __Malloc_Alloc_Template { public: //static void(*set_malloc_handler(void(*f)()))()源码给出的形式,返回值和参数类型都是函数指针 static HANDLE_FUNC SetMallocHandler(HANDLE_FUNC f)//既然在OOM_MALLOC里使用了句柄,那么就要设置 { HANDLE_FUNC old = _handle; _handle = f; return old; }//用户来设置句柄,但是不建议,容易陷入死循环 static void* OOM_Malloc(size_t size) { while (1) { if (_handle == 0) { throw bad_alloc(); } _handle();//如果设置了句柄,那么就会一直循环,直到申请到了,才会退出 void *ret = malloc(size); if (ret) { return ret; } } } static void* Allocate(size_t size)//直接调用malloc { void*ret = malloc(size); if (ret == 0)//表示一级空间配置器调用malloc没有申请到内存 { ret = OOM_Malloc(size);//失败了并没有直接返回,而是继续调用OOM_MALLOC } } static void Deallocate(void* p, size_t n)//释放一级空间配置器申请的内存 { free(p); } private: static HANDLE_FUNC _handle; };
上述就是简单的一级空间配置器的设计,当然STL设计的还有realloc在这里我只是简单的模拟实现以下,没有写realloc;
二级空间配置器的设计相较于一级空间配置器的实际就复杂一些
他申请内存并不是直接先问系统索要见下图的过程:
小细节:
template <bool threads, int inst> class __DefaultAlloceTemplate { public: static size_t FREELIST_INDEX(size_t bytes)//计算这n个字节对应的自由链表的下标 { return (((bytes)+__ALIGN - 1) / __ALIGN - 1);//STL非常注重效率 } static size_t ROUND_UP(size_t bytes) { return ((bytes)+__ALIGN - 1)&(~(__ALIGN - 1));//变成8的倍数 } static char* Chunk(size_t n, size_t &nobjs) { //__TRACE_DEBUG("到memory pool期望取%u个%u bytes对象\n", nobjs, n); size_t total_bytes = n*nobjs;//向内存池索要的总字节数,分三种情况 size_t left_bytes = _endfree - _startfree;//内存池中剩余的字节数 if (total_bytes <= left_bytes)//说明一次就可以直接从内存池中拿到n*nobjs个字节 { //__TRACE_DEBUG("memory pool拥有足够%u个对象的内存\n", nobjs) char *ret = _startfree; _startfree += total_bytes; return ret; } else if (left_bytes>n)//说明内存池中不足n*njobs个字节,但是足够n个 { //__TRACE_DEBUG("memory pool只有%u个对象的内存\n", nobjs); nobjs = left_bytes/n; //实际能申请nobjs个 total_bytes = n*nobjs; char *ret = _startfree; _startfree += total_bytes; return ret; } else//说明内存池中连n个都不够 { //__TRACE_DEBUG("memory pool连一个%ubytes的对象都没有\n", n); if (left_bytes > 0)//说明内存池中还有剩余的虽然不足n个字节,必须先处理掉,才能给内存池重新分配空间 { size_t index = FREELIST_INDEX(left_bytes); ((Obj*)(_startfree))->free_list_link = _freeList[index]; _freeList[index] = (Obj*)_startfree; } //说明要为狭义内存池分配空间了 size_t byteToGets = total_bytes * 2 + ROUND_UP(_heapsize >> 4); //__TRACE_DEBUG("到系统申请%u bytes给memory pool\n", byteToGets); _startfree = (char*)malloc(byteToGets); if (_startfree == NULL)//说明内存已经吃紧,到更大的自由链表中去获取空间 { //__TRACE_DEBUG("到系统申请内存失败\n", n); for (size_t i = FREELIST_INDEX(n); i < __NFREELISTS; ++i) { if (_freeList[i]) { Obj*ret = _freeList[i]; _freeList[i] = ret->free_list_link; _startfree = (char*)ret; _endfree = _startfree + (i + 1)*__ALIGN; return Chunk(n, nobjs); } } //如果还是没有分配到内存,也不要灰心,继续调用一级空间配置器 _startfree = (char*)__Malloc_Alloc_Template<0>::Allocate(byteToGets); } //说明一定分配内存成功了 _heapsize = _heapsize + byteToGets; _endfree = _startfree + byteToGets; return Chunk(n, nobjs); } } static void* Allocate(size_t n) { if (n > __MAX_BYTES) { return __Malloc_Alloc_Template<0>::Allocate(n); } //__TRACE_DEBUG("调用二级空间配置器申请%u bytes\n", n); size_t index = FREELIST_INDEX(n);//计算在自由链表下对应的下标 if (_freeList[index])//说明自由链表不空 { //__TRACE_DEBUG("在自由链表第%d个位置取一个对象\n", index); Obj *ret = _freeList[index]; _freeList[index] = ret->free_list_link; return ret; } //说明自由链表对应的位置没有存储空间,此时我们需要去问内存池索要; else { return Refill(ROUND_UP(n)); } } static char* Refill(size_t n)//实际上返回我们需要的,并且将剩余的挂在相应的自由链表下面 { size_t nobjs = 20; char *ret=Chunk(n, nobjs);//真正向内存池索要数据 //__TRACE_DEBUG("到memory pool申请到%u个对象,一个返回使用,剩余挂到自由链表下面\n", nobjs); if (nobjs == 1) { return ret; } else { size_t index = FREELIST_INDEX(n); _freeList[index] = (Obj*)(ret+n); Obj*cur = _freeList[index]; for (size_t i = 2; i < nobjs; ++i) { Obj*next =(Obj*)((char*)cur+n); cur->free_list_link = next; cur = next; } cur->free_list_link = NULL; return ret; } } static void Deallocate(void* ptr, size_t n) { if (n > __MAX_BYTES) { __Malloc_Alloc_Template<0>::Deallocate(ptr, n); } else//并没有真正的释放,而是挂在了自由链表下面 { size_t index = FREELIST_INDEX(n); //__TRACE_DEBUG("将释放的对象挂到freeList[%u]\n", index); ((Obj*)ptr)->free_list_link = _freeList[index]; _freeList[index] = (Obj*)ptr; } } private: enum { __ALIGN = 8 }; enum { __MAX_BYTES = 128 }; enum { __NFREELISTS = __MAX_BYTES / __ALIGN }; union Obj { union Obj * free_list_link; char client_data[1]; /* The client sees this. */ };//管理自由链表 //狭义内存池 static Obj*_freeList[__NFREELISTS]; static char*_startfree; static char*_endfree; static size_t _heapsize; };
相关文章推荐
- STL之空间配置器
- 【STL】空间配置器(二):二级空间配置器
- STL源码——特殊的空间配置器
- STL----空间配置器
- 【STL】空间配置器
- STL之空间配置器
- STL之空间配置器详细讲解
- STL的空间配置器
- STL中空间配置器的内存分配策略
- STL空间配置器——第一级配置器__malloc_alloc_template剖析
- 【STL】实现简单的空间配置器
- SGI STL 之空间配置器
- STL之空间配置器源码(框架)剖析
- STL源码学习——空间配置器
- STL 二级空间配置器
- 带你深入理解STL之空间配置器(思维导图+源码)
- STL源码学习之空间配置器allocator【2013.11.15】
- STL之空间配置器
- STL深入探究(一、空间配置器)
- 带你深入理解STL之空间配置器(思维导图+源码)