内存池原理以及代码实现简易版内存池

内存池的应用及其必要性

应用场景

当客户端连接上服务端的时候,服务端会准备一部分的堆区用来做消息保留。当一个连接成功之后,服务器会在堆区为其分配一段属于这个连接的内存,当连接关闭之后,所分配的内存也随之释放。但是当连接量较大且过于频繁时,不可避免地对内存进行频繁的分配和释放。这会导致堆区出现小窗口,也就是堆区碎片化。 1 全局的内存池 用jemalloc或者tcmalloc 开源封装好的 2 一个链接做一个内存池 这种场景,链接的生命周期没有那么长,小块没有必要回收 3 每一个消息做一个内存池 ----X 这种方式没有意义。

必要性

堆区出现碎片化会怎么样?

长时间工作会出现不可查的BUG
无法分配较大且整块的内存,malloc会返回NUL

为了减少堆区出现严重的内存碎片化,让程序能够顺利长时间的工作。程序中加一个内存池是非常有必要的。

开源内存池组件

jemalloc:
tcmalloc:内存回收的机制有一些小问题
使用方法:直接在前面加上一个宏定义 malloc 和free就被hook住了。然后在自己写的代码程序中调用malloc,实际上调用的是内存池开源库中的malloc和free。
这两种库的具体使用方法请自行去百度上搜索即可。

内存池设计原理

首先给大家贴上两张大图贴。
在这里插入图片描述
在这里插入图片描述(画的比较丑)

场景:在一段很干净的堆区上,如何实现能避免内存碎片化的内存池?

使用链表将分配出来的一块一块内存在堆区连接起来,设置flag(是否被使用),让链表节点上的各段内存慢慢各自扩张。

单独使用链表会出现什么问题?

内存块会被划分得越来越小,链表会变得越来越长,直到不能划分出更大得内存。

所以加入了固定内存的想法的设计

在这里插入图片描述

对于分配小段内存时,将小段内存进行固定划分,如下
16bytes
32bytes
64bytes
128bytes
256byts
512bytes
根据自己需要内存
如上图将各个范围的空间大小用一个数组组织起来。
首先以16byte为例。
首先一次性分配4k的内存空间挂载到16bytes上面,然后每一次需要一个16个字节空间时,直接从4k的空间中取。直到16byte上这个4k空间用完后,在分配一个4k空间用链表的方式串起来。
当链表在一个数组节点串了很多时,会发现查找空间速度会很慢。
1.查找慢
分配时查找
释放时候查找
2.块与块之间也会出现间隙
无法将块合并
小块回收麻烦

所以最后得出自定义固定小块和随机大块的内存池模型

在这里插入图片描述

//大块节点
//成员:1指向下一个大块节点的指针,2指向自己节点管理的内存空间

struct mp_large_s {
    struct mp_lagge_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 *curent;//当前还未满小块节点
    struct mp_large_s *large;//大块节点的首节点
    struct mp_node_s head[0];//小块节点的首标志
};

一个mp_pool_s 内存池管理器中 有一个指向大块节点链表的指针,根据max大小来区分用户申请的是大块内存还是小块内存。
在这里插入图片描述
流程图

内存池代码实现api

内存池的创建

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));//malloc 如果一次性分配4k 可能会失败,但是posix_memalign可以
	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);

}

小内存块创建

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;

	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;
		}
		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_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);
	
}

源代码地址及测试代码

https://github.com/xiaoyeyihao/xioayeyihao.github.io/tree/master/mempool

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值