utlis 内存池 对象池

utils 内存池

内存池负责管理多个对象池,对象池负责管理多个对象

空间非连续,用时分配

对象池

对象池类的变量 map<mid_t, T*>负责记录正在使用的内存映射,POD类型的对象池,size时确定的

UNIT_POOL_BASE
+u64 _type
+int _num_limit
+int _unit_size
+int _max_used
+string _type_name
+alloc_unit() : virtual
+get_unit() : virtual
+free_unit() : virtual
+get_cur_use_num() : virtual
UNIT_POOL
+std::map _map_use
class UNIT_POOL_BASE
{
public:
    virtual mid_t alloc_unit() = 0;
    virtual void free_unit(mid_t mid) = 0;
    virtual void* get_unit(mid_t mid) = 0;
    virtual int get_cur_use_num() = 0;

public:
    u16 _type;                       // pool type
    int _num_limit;                  // 数量限制
    int _unit_size;                  // 单个unit大小
    int _max_used;                   // 最大用了多少个
    std::string _type_name;          // type name
};

template<typename T>
class UNIT_POOL : UNIT_POOL_BASE
{
public:
    typedef typename std::map<mid_t, T*>::iterator IT;
    UNIT_POOL()
    {
        _map_use.clear();
        _num_limit = 0;
        _max_used = 0;
        _type_name = "";
    }

    ~UNIT_POOL()
    {
        for (auto& itr : _map_use)
        {
            if (itr->second)
            {
                delete (itr->second); //delete 指针时会调用指针所指对象的析构函数
            }
        }
        _map_use.clear();
    }

    virtual mid_t alloc_unit();
    virtual void free_unit(mid_t mid);
    virtual void* get_unit(mid_t mid);
    virtual int get_cur_use_num();

private:
    std::map<mid_t, T*> _map_use;  //  正在使用的内存映射
};

小技巧,使用位域在按type分配时,加入序列号信息。对象记录了自己的type可以反查到对应对象池的信息

typedef struct unitmid_t
{
    u64      type : 16;
    u64      serial : 48;
} unitmid_t;

inline mid_t alloc_mid(u16 type)
{
    static u64 base_id = 10000;
    ++base_id;

    unitmid_t unit_mid;
#pragma GCC diagnostic ignored "-Wconversion"
    unit_mid.type = type;
    unit_mid.serial = base_id;
#pragma GCC diagnostic pop
    return *(mid_t *)&unit_mid;
}

相应函数实现 分配对象 获取对象 释放对象

template<typename T>
mid_t UNIT_POOL<T>::alloc_unit()
{
    if (_num_limit <= (int)_map_use.size())
    {
        fatal_log("type:%d type_name:%s alloc fail, cur use:%d upper limit exceeded"
                    , _type, _type_name.c_str(), (int)_map_use.size());
        return INVALID_MID;
    }

    void* data = malloc(_unit_size);
    assert_retval(data, INVALID_MID);

    // placement new
    memset((char*)data, 0x00, _unit_size);
    T* ptr = new(data) T;
    assert_retval(ptr != nullptr, INVALID_MID);

    mid_t mid = alloc_mid(_type);
    _map_use[mid] = ptr;

    if (_max_used < (int)_map_use.size())
    {
        _max_used = (int)_map_use.size();
    }

    return mid;
}

#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
template<typename T>
void UNIT_POOL<T>::free_unit(mid_t mid)
{
    IT itr = _map_use.find(mid);
    if (itr == _map_use.end())
    {
        return;
    }

    T* ptr = itr->second; // 在已分配存储中构造(“布置”)
    _map_use.erase(itr);

    assert_retnone(ptr);
    ptr->~T(); // free并不会调用对象的析构,要手动调用

    // free
    free((void*)ptr);

}
#pragma GCC diagnostic warning "-Wdelete-non-virtual-dtor"

template<typename T>
void* UNIT_POOL<T>::get_unit(mid_t mid)
{
    IT itr = _map_use.find(mid);
    if (itr == _map_use.end())
    {
        return nullptr;
    }

    return itr->second;
}

template<typename T>
int UNIT_POOL<T>::get_cur_use_num()
{
    return (int)_map_use.size();
}

内存池

对象池根据编号管理对应的对象池

 std::map<int, UNIT_POOL_BASE*>  g_map_pool; //全局的内存池 装有对象池的地址

相应函数封装 分配对象 获取对象 释放对象

/**
@brief 内存池初始化和回收,进程启动和退出时调用
*/
int mempool_init();
void mempool_fini();

/**
@brief 对象类型注册
@param[in] type -- 内存对象类型
@param[string] type_name -- 内存type名字
@param[in] num_limit -- 内存数量限制
@param[UNIT_POOL_BASE] pool_fn -- 内存对象管理指针
@retval 0 -- 成功
@retval <0 -- 失败
*/
int unitpool_init(u16 type, const char* type_name, int num_limit, int unit_size, UNIT_POOL_BASE* unit_pool);

/**
@brief 申请对象 // 申请对象,调用内存池中固定类型对象池的分配对象
@param[in] type -- 内存对象类型
@retval =INVALID_MID -- 失败
@retval 其他值-- 成功,对象的mid标志
*/
mid_t memunit_alloc(u16 type);

/**
@brief 对象释放
@param[in] type -- 内存对象类型
*/
void memunit_free(mid_t mid);

/**
@brief 获得对象内存地址
@param[in] type -- 内存对象类型
@retval =nullptr -- 失败,找不到该对象
@retval !=nullptr-- 成功,对象的内存地址
*/
void* memunit_get(mid_t mid);

具体实现

int mempool_init()
{
    g_map_pool.clear();
    return 0;
}

void mempool_fini()
{
    g_map_pool.clear();
}

int unitpool_init(u16 type, const char* type_name, int num_limit, int unit_size, UNIT_POOL_BASE* unit_pool)
{
    assert_retval(unit_pool && type_name, BGERR_INVALID_ARG);
    assert_retval(type >= 0 && type < MAX_POOL_TYPE_NUM, BGERR_INVALID_ARG);
    assert_retval(num_limit > 0, BGERR_INVALID_ARG);

    auto itr = g_map_pool.find(type);
    if (itr != g_map_pool.end())
    {
        assert_retval(0, BGERR_INVALID_ARG);
    }

    unit_pool->_type = type;
    unit_pool->_num_limit = num_limit;
    unit_pool->_type_name = type_name;
    unit_pool->_unit_size = unit_size;

    g_map_pool[type] = unit_pool;

    infor_log("unitpool init, type:%d name:%s num_limit:%d", type, type_name, num_limit);

    return 0;
}

mid_t memunit_alloc(u16 type) 
{
    auto itr = g_map_pool.find(type);
    if (itr == g_map_pool.end() || !itr->second)
    {
        assert_retval(0, INVALID_MID);
    }

    return itr->second->alloc_unit();
}

void memunit_free(mid_t mid)
{
    unitmid_t *unitmid = (unitmid_t *)&mid;
    int type = unitmid->type;

    auto itr = g_map_pool.find(type);
    if (itr == g_map_pool.end() || !itr->second)
    {
        //assert_retnone(0);
        return;
    }

    itr->second->free_unit(mid);
}


void* memunit_get(mid_t mid)
{
    unitmid_t *unitmid = (unitmid_t *)&mid;
    int type = unitmid->type;

    auto itr = g_map_pool.find(type);
    if (itr == g_map_pool.end() || !itr->second)
    {
        //assert_retval(0, nullptr);
        return nullptr;
    }

    return itr->second->get_unit(mid);
}
#define POOL_REGISTER(MEM_TYPE_ID, MEM_TYPE_NAME, MEM_STRUCT_TYPE, MEM_UNIT_NUM) \
    do {\
        UNIT_POOL<MEM_STRUCT_TYPE>* unit_pool = new UNIT_POOL<MEM_STRUCT_TYPE>;\
        if (nullptr == unit_pool)\
        {\
            error_log("mem type: %s create register deal fn failed", #MEM_TYPE_ID); \
            return BGERR_UNKNOWN; \
        }\
        int ret = bg_unitpool_init(MEM_TYPE_ID, MEM_TYPE_NAME, MEM_UNIT_NUM, sizeof(MEM_STRUCT_TYPE), (UNIT_POOL_BASE*)unit_pool);\
        if (0 != ret)\
        {\
            error_log("mem type: %s register deal fn failed, retval: %d", \
                #MEM_TYPE_ID, BGERR_UNKNOWN); \
            return ret; \
        }\
    }while(0)

思考

  • 对象池用时分配,并未在连续空间上分配,如果连续空间要初始化时分配所有空间,对对象的管理可以使用map或者free_list记录,或者数组记录第几块被使用

  • 连续空间如何防止边缘部分被写坏,非连续空间段错误,连续空间加入保护部分,转型使用dynamic_cast

补充

内存池的分配算法:
伙伴算法和slab

  • 伙伴算法(将整块分配成对应的小块,回收时看看相邻的能不能合并)
  • slab(类似stl的16条free_list)
  • 特定情况只分配按池回收的(注意好内存对齐即可)

只分配按池回收的

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <fcntl.h>



#define MP_ALIGNMENT       		32
#define MP_PAGE_SIZE			4096
#define MP_MAX_ALLOC_FROM_POOL	(MP_PAGE_SIZE-1)

// 内存对齐 返回的是alignment的倍数
// mp_align(31, 32) = 32, mp_align(32, 32) = 32, mp_align(33, 32) = 64
#define mp_align(n, alignment) (((n)+(alignment-1)) & ~(alignment-1))
#define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1))





struct mp_large_s {
	struct mp_large_s *next;
	void *alloc;
};

struct mp_node_s {

	unsigned char *last;
	unsigned char *end;
	
	struct mp_node_s *next;
	size_t failed;          //尝试失败次数
};

struct mp_pool_s {

	size_t max;

	struct mp_node_s *current;  // 最前面可用的节点
	struct mp_large_s *large;

	struct mp_node_s head[0];

};

struct mp_pool_s *mp_create_pool(size_t size);
void mp_destory_pool(struct mp_pool_s *pool);
void *mp_alloc(struct mp_pool_s *pool, size_t size);
void *mp_nalloc(struct mp_pool_s *pool, size_t size);
void *mp_calloc(struct mp_pool_s *pool, size_t size);
void mp_free(struct mp_pool_s *pool, void *p);


struct mp_pool_s *mp_create_pool(size_t size) {

	struct mp_pool_s *p;
	int ret = posix_memalign((void **)&p, MP_ALIGNMENT, size + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s));
	if (ret) {
		return NULL;
	}
	
	p->max = (size < MP_MAX_ALLOC_FROM_POOL) ? size : MP_MAX_ALLOC_FROM_POOL;
	p->current = p->head;
	p->large = NULL;

	p->head->last = (unsigned char *)p + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);
	p->head->end = p->head->last + size;

	p->head->failed = 0;

	return p;

}

void mp_destory_pool(struct mp_pool_s *pool) {

	struct mp_node_s *h, *n;
	struct mp_large_s *l;

	for (l = pool->large; l; l = l->next) {
		if (l->alloc) {
			free(l->alloc);
		}
	}

	h = pool->head->next;

	while (h) {
		n = h->next;
		free(h);
		h = n;
	}

	free(pool);

}

void mp_reset_pool(struct mp_pool_s *pool) {

	struct mp_node_s *h;
	struct mp_large_s *l;
	// 释放所有大内存
	for (l = pool->large; l; l = l->next) {
		if (l->alloc) {
			free(l->alloc);
		}
	}

	pool->large = NULL;
	// 重置所有小块内存区,可以重用
	for (h = pool->head; h; h = h->next) {
		h->last = (unsigned char *)h + sizeof(struct mp_node_s);
	}

}

static void *mp_alloc_block(struct mp_pool_s *pool, size_t size) {

	unsigned char *m;
	struct mp_node_s *h = pool->head;
	size_t psize = (size_t)(h->end - (unsigned char *)h);
	
	int ret = posix_memalign((void **)&m, MP_ALIGNMENT, psize);
	if (ret) return NULL;

	struct mp_node_s *p, *new_node, *current;
	new_node = (struct mp_node_s*)m;

	new_node->end = m + psize;
	new_node->next = NULL;
	new_node->failed = 0;

	m += sizeof(struct mp_node_s);
	m = mp_align_ptr(m, MP_ALIGNMENT);
	new_node->last = m + size;

	current = pool->current;
	// 这里的循环用来找最后一个链表节点,这里failed用来控制循环的长度,如果分配失败次数达到5次,
	// 就忽略,不需要每次都从头找起
	for (p = current; p->next; p = p->next) {
		if (p->failed++ > 4) {
			current = p->next;
		}
	}
	p->next = new_node;

	pool->current = current ? current : new_node;

	return m;

}

static void *mp_alloc_large(struct mp_pool_s *pool, size_t size) {

	void *p = malloc(size);
	if (p == NULL) return NULL;

	size_t n = 0;
	struct mp_large_s *large;
	for (large = pool->large; large; large = large->next) {
		if (large->alloc == NULL) {
			large->alloc = p;
			return p;
		}
		// 为了提高效率, 如果在三次内没有找到空的large结构体,则创建一个
		if (n ++ > 3) break;
	}

	large = mp_alloc(pool, sizeof(struct mp_large_s));
	if (large == NULL) {
		free(p);
		return NULL;
	}

	// 插入头结点
	large->alloc = p;
	large->next = pool->large;
	pool->large = large;

	return p;
}

void *mp_memalign(struct mp_pool_s *pool, size_t size, size_t alignment) {

	void *p;
	
	int ret = posix_memalign(&p, alignment, size);
	if (ret) {
		return NULL;
	}

	struct mp_large_s *large = mp_alloc(pool, sizeof(struct mp_large_s));
	if (large == NULL) {
		free(p);
		return NULL;
	}

	large->alloc = p;
	large->next = pool->large;
	pool->large = large;

	return p;
}




void *mp_alloc(struct mp_pool_s *pool, size_t size) {

	unsigned char *m;
	struct mp_node_s *p;

	if (size <= pool->max) {

		p = pool->current;

		do {
			
			m = mp_align_ptr(p->last, MP_ALIGNMENT);
			if ((size_t)(p->end - m) >= size) {
				p->last = m + size;
				return m;
			}
			p = p->next;
		} while (p);

		return mp_alloc_block(pool, size);
	}

	return mp_alloc_large(pool, size);
	
}


void *mp_nalloc(struct mp_pool_s *pool, size_t size) {

	unsigned char *m;
	struct mp_node_s *p;

	if (size <= pool->max) {
		p = pool->current;

		do {
			m = p->last;
			if ((size_t)(p->end - m) >= size) {
				p->last = m+size;
				return m;
			}
			p = p->next;
		} while (p);

		return mp_alloc_block(pool, size);
	}

	/*
	* 比如来了个8K,找到空闲给他分配,如果不够再分配,
	* 回收的时候不做释放,后面再次使用的时候直接拿出来使用。
	**/
	return mp_alloc_large(pool, size);
	
}

void *mp_calloc(struct mp_pool_s *pool, size_t size) {

	void *p = mp_alloc(pool, size);
	if (p) {
		memset(p, 0, size);
	}

	return p;
	
}

// 仅仅释放了大块内存,小块内存不释放,内存池销毁时同一释放
void mp_free(struct mp_pool_s *pool, void *p) {

	struct mp_large_s *l;
	for (l = pool->large; l; l = l->next) {
		if (p == l->alloc) {
		/*
		* 遍历large链表,释放large内存块,
		* 注意此处并没有释放管理large内存块的内存(即large链表节点),
		* 而仅是将其alloc指针设为NULL
		**/
			free(l->alloc);
			l->alloc = NULL;

			return ;
		}
	}
	
}


int main(int argc, char *argv[]) {

	int size = 1 << 12;

	struct mp_pool_s *p = mp_create_pool(size);

	int i = 0;
	for (i = 0;i < 10;i ++) {

		void *mp = mp_alloc(p, 512);
//		mp_free(mp);
	}

	//printf("mp_create_pool: %ld\n", p->max);
	printf("mp_align(123, 32): %d, mp_align(17, 32): %d\n", mp_align(24, 32), mp_align(17, 32));
	//printf("mp_align_ptr(p->current, 32): %lx, p->current: %lx, mp_align(p->large, 32): %lx, p->large: %lx\n", mp_align_ptr(p->current, 32), p->current, mp_align_ptr(p->large, 32), p->large);

	int j = 0;
	for (i = 0;i < 5;i ++) {

		char *pp = mp_calloc(p, 32);
		for (j = 0;j < 32;j ++) {
			if (pp[j]) {
				printf("calloc wrong\n");
			}
			printf("calloc success\n");
		}
	}

	//printf("mp_reset_pool\n");

	for (i = 0;i < 5;i ++) {
		void *l = mp_alloc(p, 8192);
		mp_free(p, l);
	}

	mp_reset_pool(p);

	//printf("mp_destory_pool\n");
	for (i = 0;i < 58;i ++) {
		mp_alloc(p, 256);
	}

	mp_destory_pool(p);

	return 0;

}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值