-
[原创]收集专用高效内存池,支撑多线程.原创,非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));
}
}