 
- 
   [原创]收集专用高效内存池,支撑多线程.原创,非sgi的内存池添加时间:2013-5-28 点击量:起首要申明一点,这个内存池应用时须要重视的,若是想用在长久分派出去的内存,请慎用. 
 因为假如一个区块被分派完了,只有在这个区块里已分派的内存被完全开释后,这个区块才干重用.因为当初是设计为收集分包用的内存池.为了效力而采取这个策略的. 发代码之前先简单介绍下内存池的思路. 
 内存池分256个区块,编号为0~255
 区块的布局为:区块记录了3个信息一个指针 _left_mem是残剩的memory,初始化为区块内存总长 _alloc_org是指向区块有效内存的肇端点.初始化为0(全部区块一开端都是可以有效分派出去的) _need_to_rel是须要开释的内存.初始化为区块内存总长 
 内存块先简单介绍.后面再具体说实现细节//区块,用于保存未分派内存(被分别出去) 
 struct _chunk{
 int _left_mem; //此chunk残剩长度
 int _alloc_org; //指向可用于分派内存快头的指针
 int _need_to_rel;//须要开释几许内存
 
 char _mem[1]; //此chunk持有的内存区
 };而分派出去的块用以下布局标识 struct _mem_piece{ 
 
 int _mp_len; //内存块大小
 char _chunk_index; //内存块所属区块的索引
 
 char _mem[1]; //内存块的内存首地址
 };
 
 内存池的所有区块可以分为三个区域无效区:这里的区块已经完全分派完毕,守候开释,开释完成后会到区 待分派区:这个区有且只有一个块,分派内存都邑从这里抽取 区: 下图是内存池的状况: 
  
 内存池一开端会处于以下的状况 
 初始化的时辰第一个区块会被初始化.持续分派就会耗尽绿色的待分派块,内存池会把它归入无效区,同时测验测验从体系创建一个新的区块并列为待分派块
 只有无效区的内存的块被完全开释后才会转到区.当然,若是区有区块的话,就不须要新建区块,而是直接指定区的第一个区块为待分派区块
 然后我再来申明一下分派的策略._left_mem一开端就是该区块总内存大小这个不须要多说了.
 malloc被调用时:
 1.当_left_mem比请求分派的内存多的时辰.把当前_alloc_org处罚配出去,削减_left_mem和移动_alloc_org.2.a.当_left_mem不足的时辰,_left_mem(剩下的内存)将成为碎片,策画need_to_rel -= _left_mem,此区块将移动到无效区,并且内存池将获取一个新的区块(若是区有则直接获取,不然创建一个) 分派出去的时辰都履行以下的代码: 则实际上须要的内存大小是sizeof(int) + sizeof(char) + memory size 
 而客户所获得的内存是从mp->_mem开端的.所以,free回来的内存只要向前移动5个字节就能获取到分派出去的_mem_piece布局了_mem_piece mp = (_mem_piece )&p->_mem[p->_alloc_org]; 
 //内存块真正的大小
 mp->_mp_len = need_len;
 //chunk标识
 mp->_chunk_index = _S_cur_valid_index;
 
 return mp->_mem;
 b.策画need_to_rel -= _left_mem后发明need_to_rel==0则不是移动到无效区而是直接移动到区. free被调用时: 
 _need_to_rel-=被开释的内存大小,发明_need_to_rel == 0则直接把此块移动到区free调用的时辰是如许获取到回来的内存大小的 _mem_piece pmem = (_mem_piece )((char )p - _mem_piece::_mem_piece_struct_size);_mem_piece记录了这块被分派的内存有多大.直接经由过程pmem->_mp_len获取,pmem->_chunk_index则记录了属于哪个区块的,如许便可以直接归并回该区块了. shrink被调用时: 把所有处于区的区块开释,调用c标准库的free把内存还给体系,阁下瘦身收敛的结果. _need_to_rel仅仅是一个标识表记标帜.在调用free的时辰若是发明_need_to_rel==0了就直接把这个区块丢到区里面.因为终极分派完这个区块今后须要开释的值应当为 区块总内存 - 残剩碎片.所以_need_to_rel一开端就是区块总内存了.在方才绿色的待分派被完全耗尽今后,在转入无效区之前,管帐算出碎片大小(就是_left_mem)这个时辰履行need_to_rel -= _left_mem策画出真实的需开释值.策画完后,法度若是发明need_to_rel==0不会归入无效区而是直接转到区 最后总结下这个内存池的优毛病: 长处: 1.分派的时辰仅仅移动了指针,内存不足时(即创建新块的时辰)调用到c运行库的malloc.若是不算上malloc的话.内存池分派只须要履行少于10个指令. 
 所以分派速度很是快2.开释的时辰不须要归并这个步调.很多内存分派器都是在归并上用了很多时候.这个内存池不须要任何归并的动作 3.可以分派随便率性大小的内存.当然,太大的时辰会委托给malloc 毛病: 1.若是分派完一个区块后会造成少量碎片 2.无效区域内的内存只有等完全开释了,这个块才可以重用,所以不合适长久占领内存池的内存. 这个很大限度限制了这个内存池的实用性. 然后贴个测试: #include fish_mem_pool.h 
 #include <boost/timer.hpp>
 #include <iostream>
 int main()
 {
 boost::timer t;
 for (int i = 0; i < 1024 600; ++i)
 char p = (char )fishlib::fish_mem_pool::malloc(1024);
 
 std::cout << used << t.elapsed() << seconds << std::endl;
 
 getchar();
 return 0;
 }成果:   
 最后上代码: 设备内存池的头文件: 重视!内存池最大内存大小为256 _new_chunk_len 这里为 256 4096000 = 1000MB fish_mem_pool_config.h 
 / @fish_mem_pool_config.h ---------------------------------------------- 
 Copyright (c) TCC 2013
 Project :
 fish & Date : 2012/09/06
 Files :
 Brief : 按照列举值,和宏定义改变fish_mem_pool的设备
 Update :
 Note :
 -----------------------------------------------------------------/
 
 #ifndef _FISHLIB_FISH_MEM_POOL_CONFIG_H_
 #define _FISHLIB_FISH_MEM_POOL_CONFIG_H_
 
 #define _MULTI_THREAD_ //定义多线程景象
 
 
 //这里是采取了boost做多线程同步,可以自行更改
 //然则必须合适lock和unlock的接口
 //多线程宏定义
 #ifdef _MULTI_THREAD_
 #include <boost/thread/mutex.hpp>
 //#include <boost/signals2/mutex.hpp>
 typedef boost::mutex _LOCK_CORE;
 #endif //_MULTI_THREAD_
 
 
 namespace fishlib{
 enum _config_val{
 _max_len = 8192, //内存池能分派的最大内存数,大于这个数的全都应用malloc
 
 _new_chunk_len = 4096000 //新chunk的长度
 //因为只有256个区块,所以内存池最多能分派_new_chunk_len 256的内存
 //请衡量_max_len / _new_chunk_len的值,这个值越大则一个chunk的哄骗率可能会降落
 //_max_len / _new_chunk_len越小,则chunk的哄骗率越高
 //尽量对峙在1/2以下
 };
 }
 
 #endif //_FISHLIB_FISH_MEM_POOL_CONFIG_H_
 
 
 内存池的声明头文件: fish_mem_pool.h / @fish_mem_pool.h ---------------------------------------------- 
 Copyright (c) TCC 2013
 Project :
 fish & Date : 2012/09/06
 Files :
 Brief :
 Update :
 Note :
 -----------------------------------------------------------------/
 #ifndef _FISHLIB_FISH_MEM_POOL_H_
 #define _FISHLIB_FISH_MEM_POOL_H_
 
 
 namespace fishlib{
 
 class fish_mem_pool{
 public:
 /
 大于_max_len的直接用malloc分派,分派失败返回NULL
 /
 static void malloc(int len);
 
 /
 由fishlib::fish_mem_pool::malloc分派的都要用这个函数开释
 /
 static void free(void p);
 
 /
 手动紧缩,调用后内存池会把余暇块还给体系,返回开释了几许内存
 /
 static int shrink();
 };
 }
 
 #endif //_FISHLIB_FISH_MEM_POOL_H_
 内存池实现文件: fish_mem_pool.cpp / @fish_mem_pool.cpp ---------------------------------------------- 
 Copyright (c) TCC 2013
 Project :
 fish & Date : 2012/09/06
 Files :
 Brief :
 Update :
 Note :
 -----------------------------------------------------------------/
 #include fish_mem_pool.h
 #ifndef _FISHLIB_FISH_MEM_POOL_CONFIG_H_
 #include fish_mem_pool_config.h
 #endif //_FISHLIB_FISH_MEM_POOL_CONFIG_H_
 #include <stdlib.h>
 #include <memory.h>
 #include <assert.h>
 
 
 //模板类,益处是若是没有效到则不会生成,甚至不会检测语法错误
 template<typename T>
 class _MEM_POOL_LOCK{
 public:
 //主动加锁
 _MEM_POOL_LOCK(T &t_lock): _t_lock(t_lock){
 _t_lock.lock();
 }
 //主动在跳出感化域时解锁
 ~_MEM_POOL_LOCK(){
 _t_lock.unlock();
 }
 private:
 T &_t_lock;
 };
 
 
 //多线程宏定义
 #ifdef _MULTI_THREAD_
 
 static _LOCK_CORE _S_lc;
 #define FISH_LOCK _MEM_POOL_LOCK<_LOCK_CORE> mpl(_S_lc);
 
 #ifdef _DEBUG
 static _LOCK_CORE _S_dbg_lc;
 #define FISH_DBG_LOCK _MEM_POOL_LOCK<_LOCK_CORE> mpl(_S_dbg_lc);
 #endif //_DEBUG
 
 #else
 //在非多线程状况下,此宏定义不会做任何工作
 #define FISH_LOCK
 
 #ifdef _DEBUG
 #define FISH_DBG_LOCK
 #endif //_DEBUG
 
 #endif //_MULTI_THREAD_
 
 namespace fishlib{
 
 enum default_val{
 _max_chunk_cnt = 256 //最大区块数,默认值,只能是小于256的数字
 };
 
 
 //内存池全局变量
 //记录内存池状况
 struct _chunk;
 static _chunk _S_chunk_list[_max_chunk_cnt]; //区块表,记录所有区块
 static unsigned char _S_cur_valid_index = 0; //当前指向的有效区块,有效区块有且只有一个
 static unsigned char _S_invalid_index_s = 0; //第一个无效的区块
 static unsigned char _S_invalid_cnt = 0; //无效区块总数
 static unsigned char _S_free_index_s = 0; //区开端索引
 
 static int _S_total_chunk_cnt = 0; //统共的区块数
 
 
 
 //////////////////////////////////////////////////////////////////////////
 
 //内存池内部应用布局定义
 
 
 #pragma pack(1)
 
 //区块,用于保存未分派内存(被分别出去)
 struct _chunk{
 
 enum{
 _chunk_struct_size = sizeof(int) + sizeof(int) + sizeof(int)
 };
 
 int _left_mem; //此chunk残剩长度
 int _alloc_org; //指向可用于分派内存快头的指针
 int _need_to_rel;//须要开释几许内存
 
 char _mem[1]; //此chunk持有的内存区
 
 inline static _chunk new_chunk(){
 if (_S_total_chunk_cnt < _max_chunk_cnt){
 //若是总区块数小于最大限制则可以分派
 _chunk p = (_chunk )::malloc(_chunk_struct_size + _new_chunk_len);
 
 if (p == NULL)
 //体系内存不足
 return NULL;
 
 //终极分派成功
 ++_S_total_chunk_cnt;
 p->_alloc_org = 0;
 p->_left_mem = _new_chunk_len;
 p->_need_to_rel = _new_chunk_len;
 return p;
 }else{
 return NULL;
 }
 }
 
 inline static void _chunk(_chunk p){
 //一个chunk烧毁,并且把内存交还给体系
 --_S_total_chunk_cnt;
 ::free(p);
 }
 };
 
 //内存块,用于记录已分派内存
 struct _mem_piece{
 enum{
 _mem_piece_struct_size = sizeof(char) + sizeof(int)
 };
 
 int _mp_len; //内存块大小
 char _chunk_index; //内存块所属区块的索引
 
 char _mem[1]; //内存块的内存首地址
 };
 #pragma pack()
 
 //////////////////////////////////////////////////////////////////////////
 
 //用于内存池主动初始化的布局
 struct _init_mem_pool{
 _init_mem_pool(){
 //内存池初始化代码
 memset(_S_chunk_list, 0, _max_chunk_cnt sizeof(_chunk )); //清零
 _S_chunk_list[0] = _chunk::new_chunk();
 _S_cur_valid_index = 0;
 _S_invalid_index_s = 0;
 _S_invalid_cnt = 0;
 }
 ~_init_mem_pool(){
 
 }
 };
 
 //自消息态对象,在此调用初始化代码
 static _init_mem_pool imp;
 
 
 //助手函数
 //从一个chunk中获取内存
 static inline void _get_mem(int need_len){
 //取出当前有效chunk
 _chunk p = _S_chunk_list[_S_cur_valid_index];
 
 if (p->_left_mem <= need_len){
 //内存不足
 //因为在残剩空间刚巧便是的时辰景象斗劲错杂,故舍弃这种可能
 return NULL;
 }else{
 
 _mem_piece mp = (_mem_piece )&p->_mem[p->_alloc_org];
 
 //抽取内存
 //残剩内存较少
 //指向可用于分派内存快头的指针增长
 p->_left_mem -= need_len;
 p->_alloc_org += need_len;
 
 
 //内存块真正的大小
 mp->_mp_len = need_len;
 //chunk标识
 mp->_chunk_index = _S_cur_valid_index;
 
 return mp->_mem;
 }
 }
 
 //获得区块总长
 static inline int _get_free_chunk_cnt(){
 return _S_total_chunk_cnt - (_S_invalid_cnt + 1);
 }
 
 //增长_S_cur_valid_index
 static inline unsigned char _inc_cur_idx(unsigned char idx){
 ++idx;
 
 if (_S_chunk_list[idx] == NULL){
 idx -= _S_total_chunk_cnt;
 }
 
 return idx;
 }
 
 //使开释无效区块,成为区块
 static inline void _free_invalid_chunk(unsigned char index){
 //把要成为chunk的区块变为初始状况
 _S_chunk_list[index]->_alloc_org = 0;
 _S_chunk_list[index]->_left_mem = _new_chunk_len;
 _S_chunk_list[index]->_need_to_rel = _new_chunk_len;
 
 //与第一个无效区块互换,再++_S_invalid_index_s使其挪出无效区域
 _chunk ptmp = _S_chunk_list[index];
 _S_chunk_list[index] = _S_chunk_list[_S_invalid_index_s];
 _S_chunk_list[_S_invalid_index_s] = ptmp;
 
 _S_invalid_index_s = _inc_cur_idx(_S_invalid_index_s);
 --_S_invalid_cnt;
 }
 
 
 //真正移动块
 static inline void _in_next_chunk(){
 //须要开释的内存 - 残剩内存,意义:残剩的内存不须要开释
 unsigned char index = _S_cur_valid_index;
 _chunk p = _S_chunk_list[index];
 
 int need_to_rel = p->_need_to_rel;
 need_to_rel -= p->_left_mem;
 p->_need_to_rel = need_to_rel;
 
 //移动到下一个
 _S_cur_valid_index = _inc_cur_idx(_S_cur_valid_index);
 //扩充无效区域
 ++_S_invalid_cnt;
 
 
 assert(p->_need_to_rel >= 0 && p->_need_to_rel <= _new_chunk_len);
 
 if (p->_need_to_rel == 0){
 //若是内存块其他分派出去的都被用完了
 //那就让它直接成为chunk
 _free_invalid_chunk(index);
 }
 }
 
 //测验测验移动区块
 //失败返回false
 static inline bool _next_chunk(){
 if (_get_free_chunk_cnt() == 0){
 //区耗尽,须要抽取内存
 if (_S_total_chunk_cnt >= _max_chunk_cnt){
 //无法抽取函数,池已满
 return false;
 }else{
 int idx = _S_cur_valid_index + 1;
 _chunk p = _chunk::new_chunk();
 if (p == NULL){
 //体系内存已经耗尽了
 return false;
 }else{
 //初始化新地位
 _S_chunk_list[idx] = p;
 //移动到新地位
 _in_next_chunk();
 return true;
 }
 }
 }else{
 //应用块
 _in_next_chunk();
 _S_free_index_s = _inc_cur_idx(_S_free_index_s);
 return true;
 }
 }
 
 
 //内存不足返回NULL
 void fish_mem_pool::malloc(int ilen){
 assert(ilen > 0);
 int real_need_len = ilen + _mem_piece::_mem_piece_struct_size;
 
 if (real_need_len > _max_len){
 //请求的内存太大
 //委托给malloc
 _mem_piece p = (_mem_piece )::malloc(real_need_len);
 if (p == NULL)
 return NULL;
 p->_chunk_index = 0;
 p->_mp_len = real_need_len;
 return p->_mem;
 }else{
 //加锁
 FISH_LOCK
 
 void p = _get_mem(real_need_len);
 if (p == NULL){
 //当前chunk的内存已经耗尽,测验测验移动到下一个chunk
 bool succeed = _next_chunk();
 if (succeed){
 return _get_mem(real_need_len);
 }else{
 return NULL;
 }
 }else{
 return p;
 }
 }
 }
 
 void fish_mem_pool::free(void p){
 _mem_piece pmem = (_mem_piece )((char )p - _mem_piece::_mem_piece_struct_size);
 if (pmem->_mp_len <= _max_len){
 //加锁
 FISH_LOCK
 
 unsigned char cindex = pmem->_chunk_index;
 _S_chunk_list[cindex]->_need_to_rel -= pmem->_mp_len;
 if (_S_chunk_list[cindex]->_need_to_rel == 0){
 //发明须要开释的内存已经开释完,则让chunk变为chunk
 _free_invalid_chunk(cindex);
 }
 }else{
 //内存块太大,是委托给malloc的内存块
 ::free(pmem);
 }
 }
 
 int fish_mem_pool::shrink(){
 
 //加锁
 FISH_LOCK
 
 int free_cnt = _get_free_chunk_cnt();
 int cnt = 0;
 unsigned char idx = 0;
 while (cnt != free_cnt){
 if (_S_chunk_list[idx] != NULL){
 if (_S_chunk_list[idx]->_left_mem == _new_chunk_len){
 _chunk::_chunk(_S_chunk_list[idx]);
 _S_chunk_list[idx] = NULL;
 ++cnt;
 }
 }
 ++idx;
 }
 
 
 return (free_cnt (_chunk::_chunk_struct_size + _new_chunk_len));
 }
 
 }
 
 
 真正的心灵世界会告诉你根本看不见的东西,这东西需要你付出思想和灵魂的劳动去获取,然后它会照亮你的生命,永远照亮你的生命。——王安忆《小说家的十三堂课》
 
 
  
   
 

 
 


