博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
STL二级空间配置器
阅读量:6800 次
发布时间:2019-06-26

本文共 5688 字,大约阅读时间需要 18 分钟。

STL的二级空间配置器类似于memory_pool,但是并不是memory_pool,因为STL的二级空间配置器只维护了16个free_list,而且只是小于128byte大小的小区块,大于128byte会调用一级空间配置器malloc。16个链表放在数组中,每一个数组所链的链表代表大小相等的小区块,因为分配的时候,只会申请8的倍数大小的空间,所以小于128byte的区块肯定是属于free_list里面。当小区块释放时,就会挂到free_list中。

下面是二级空间配置器的源码:

template <bool threads, int inst>class __default_alloc_template

_ALIGN = 8,表示最小的小区块的大小
_MAX_BYTES = 128 表示最大的区块的大小
_NFREELISTS = 16 表示free_list的个数
#if ! (defined(__SUNPRO_CC) || defined(__GNUC__))    enum {_ALIGN = 8};    enum {_MAX_BYTES = 128};    enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN# endif
这是将申请的大小按8字节对齐:
static size_t  _S_round_up(size_t __bytes)     { return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }

这是free_list的Node结构体:

__PRIVATE:  union _Obj {        union _Obj* _M_free_list_link;        char _M_client_data[1];    /* The client sees this.        */  };

free_list的定义:

# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)    static _Obj* __STL_VOLATILE _S_free_list[];         // Specifying a size results in duplicate def for 4.1# else    static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS];  //volatile 是为了防止在多线程下产生不可预期的后果# endif

内存池的相关信息:

static char* _S_start_free; //内存池中未分给free_pool的首地址 static char* _S_end_free; //内存池的末尾地址 static size_t _S_heap_size;//内存池的总空间大小

二级空间配置器分配函数:

static void* allocate(size_t __n)  {    void* __ret = 0;    //如果size大于128byte就使用一级配置器    if (__n > (size_t) _MAX_BYTES) {      __ret = malloc_alloc::allocate(__n);    }    else {      _Obj* __STL_VOLATILE* __my_free_list          = _S_free_list + _S_freelist_index(__n); //找到size对应的list      // Acquire the lock here with a constructor call.      // This ensures that it is released in exit or during stack      // unwinding.#     ifndef _NOTHREADS      /*REFERENCED*/      _Lock __lock_instance;//有关线程安全#     endif      _Obj* __RESTRICT __result = *__my_free_list; //指向size所在链表头       if (__result == 0)        __ret = _S_refill(_S_round_up(__n));  //当这个链表为空时,重新创建一个小chunk      else {        *__my_free_list = __result -> _M_free_list_link; //将链第一个节点取下来        __ret = __result; //给用户返回取下来的小区块的地址      }    }    return __ret; };

下面的函数是从一级空间配置器(malloc)申请小块内存的函数:

template 
void*__default_alloc_template<__threads, __inst>::_S_refill(size_t __n){ int __nobjs = 20; /*默认的建议值,在申请小内存块的时候,一次申请的个数,这并不是最终个数,会根据内存池剩下的空间大小进行调整*/ char* __chunk = _S_chunk_alloc(__n, __nobjs);//从内存池中申请_nobjs个_n大小的空间 _Obj* __STL_VOLATILE* __my_free_list; //指向free_list的指针 _Obj* __result; _Obj* __current_obj; _Obj* __next_obj; int __i; /*如果在_S_chunk_alloc中只申请到了一块这样大的空间,则直接返回*/ if (1 == __nobjs) return(__chunk); __my_free_list = _S_free_list + _S_freelist_index(__n); //找到该大小所对应的free_list /*将得到的多块_n大小的chunk,第一块返回给用户,剩下的加到_n所对应的free_list中*/ __result = (_Obj*)__chunk; *__my_free_list = __next_obj = (_Obj*)(__chunk + __n); for (__i = 1; ; __i++) { __current_obj = __next_obj; __next_obj = (_Obj*)((char*)__next_obj + __n); if (__nobjs - 1 == __i) { __current_obj -> _M_free_list_link = 0; break; } else { __current_obj -> _M_free_list_link = __next_obj; } } return(__result);}

_S_chunk_alloc:

template 
char*__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size, int& __nobjs){ char* __result; size_t __total_bytes = __size * __nobjs; //想要开辟的总大小 size_t __bytes_left = _S_end_free - _S_start_free; //内存池的剩余大小/*判断内存池剩余的空间和需要申请的空间的大小比较*/ if (__bytes_left >= __total_bytes) { __result = _S_start_free; //大于就可以直接分配 _S_start_free += __total_bytes; return(__result); } else if (__bytes_left >= __size) { __nobjs = (int)(__bytes_left/__size);//调整需要申请的块数 //使用内存池剩余空间分配大小为_size的内存块,能分多少块就分多少块 __total_bytes = __size * __nobjs; __result = _S_start_free; _S_start_free += __total_bytes; //将内存池的start往下移,分配空间 return(__result); } else { size_t __bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4); //内存吃没有足够的空间分配时,需要重新申请空间,这里的size就是需要向一级空间配置器申请的空间大小 if (__bytes_left > 0) { _Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__bytes_left);//将内存池剩余的小于_size的空间挂到对应大小的free_list上。 ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list; *__my_free_list = (_Obj*)_S_start_free; } /*内存池剩余为0时,malloc一个小块内存,进行分配*/ _S_start_free = (char*)malloc(__bytes_to_get); if (0 == _S_start_free) { size_t __i; _Obj* __STL_VOLATILE* __my_free_list; _Obj* __p; /*如果这是第一次申请内存池空间,则进行循环分配小块空间*/ for (__i = __size; __i <= (size_t) _MAX_BYTES; __i += (size_t) _ALIGN) { __my_free_list = _S_free_list + _S_freelist_index(__i); __p = *__my_free_list; if (0 != __p) { *__my_free_list = __p -> _M_free_list_link; _S_start_free = (char*)__p; _S_end_free = _S_start_free + __i; return(_S_chunk_alloc(__size, __nobjs)); } } //将申请的空间分配完之后,再申请一块内存池空间作为备用 _S_end_free = 0; _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get); } /*如果不是第一次申请内存池空间,把内存池的大小扩大,再调用自己进行空间分配*/ _S_heap_size += __bytes_to_get; _S_end_free = _S_start_free + __bytes_to_get; return(_S_chunk_alloc(__size, __nobjs)); }}

这就是整个STL库中的二级空间配置器的流程,其实也在模仿ptmalloc的内存管理机制,使用内存池和空间回收,避免多次库函数调用或者系统调用,大大节省了程序运行的时间。这里的二级空间配置器只要用到了free_list和小块内存池,双层保证小块空间分配的速率比直接malloc快。但是在实战中的效率对比还是需要实践才能得出结论。

转载地址:http://diywl.baihongyu.com/

你可能感兴趣的文章
go语言学习-变量的问题
查看>>
Yii2获取配置文件信息
查看>>
配置管理小报110228-2:在linux上自动更新同步系统时间方法
查看>>
NetWorker+mhvtl后端结合公司的消冗文件系统为啥不能消冗?
查看>>
Tsung学习笔记
查看>>
(进阶)数据库集群的分布式事务、两阶段提交协议、三阶提交协议
查看>>
Linux/Unix下ODBC的安装、配置与编程
查看>>
Skynet 通过组播(Multicast)实现一个简单的世界频道
查看>>
HTML5定稿一周年,你必须要重新认识HTML5了
查看>>
Anti-Anti-Spider
查看>>
Java 序列化的高级认识
查看>>
WSL 编程环境配置
查看>>
Reveal配置及上架前配置
查看>>
MySQL可使用GRANT和REVOKE的权限设置
查看>>
iOS应用架构谈 好文
查看>>
Hexo 搭建个人博客
查看>>
Java多态对象的类型转换
查看>>
N*M网格中两对角有多少种不同的路径?(递归)
查看>>
迷宫出逃
查看>>
据说这样可以改变谷歌浏览器的滚动条的样式
查看>>