内存管理-内存池的实现

1 前言

内存池出现的意义比较重大,对于服务器这种需要长时间工作的设备内存池具有比较大的优势:一个是可以避免内存频繁的分配和释放,造成内存的碎片,久而久之内存分配失败。这里实现内存池比较简易主要是为了了解内存的管理策略,如果是商用建议采用开源框架ptmalloc、tcmalloc或者jemalloc等等。

2 内存池的原理

内存池的管理,首先需要有大块内存和小块内存管理的问题,大块内存基本对于内存没有影响,因为本来就比较大不存在分配后出现内存碎片的问题,小块内存,则不一样每次分配和释放都会容易造成内存的碎片问题。所以讨论内存管理主要是对小块内存的管理,下面探讨内存管理的几种方案:
注意: 内存大小区分:没有标准,一般4K一下为小块内存,当然实际情况也可以调整。

2.1 内存利用链表进行管理

每次分配将地址加入到链表中,释放的时候不去真正释放而是,将标志位置位0,供下一次分配。
存在问题:

  • 当下次分配的大小不是在链表中的大小,这时候需要将节点拆分为两个节点,这个过程比较复杂,
  • 后面的小节点太小无法再次利用。
  • 链表查找效率笔记低
    在这里插入图片描述

2.2 分配固定大小

按照固定大小进行分配,分别为32字节,64字节,128字节,256字节和512字节大小;所有配置利用将不足大小按照向上去近似进行分配,防止链表出现的小的碎片问题。
存在问题:

  • 速度慢,在插入前需要进行一次flag为0的查找;在删除需要做一次地址匹配的查找;
  • 内存浪费的问题,就是分配字节不足时每次分配的多分配内存;
  • 链表节点之间地址不连续,没办法组成一个大块内存进行分配
    在这里插入图片描述

2.3 按块进行内存管理

内存块管理采用的方式是进行将内存分配采用大块和小块两部分分别管理,大块比较简单,分配多少就多少,直接由内存malloc分配,释放也是直接free释放。对于小块内存管理则是采用一次性分配4k大小,一个指针指向分配的起始节点后面的起始位置,另一个指针指向节点的结束位置,中间分配内存时last指针跟随移动。
存在问题:

  • 小块内存没有办法单独回收只能用于特定的场景,比如会话的声明周期。

在这里插入图片描述

3 内存池的实现

这里即采用的是按块进行内存管理的内存池方案,用于特定的连接生命周期这种局部性的内存池操作。

3.1 内存池的创建

内存池初始化,首先分配第一块内存块,这里需要注意的是内存池对象和小块内存是连续内存,一般剩余空间按照4k-1的大小分配空间。

mp_pool *mp_pool_create(size_t size)
{

	mp_pool *pool = NULL;
	mp_small_node *small = NULL;

	int ret;
	
	ret = posix_memalign((void **)&pool, MP_ALIGNMENT, size + sizeof(mp_pool) + sizeof(mp_small_node));
	if(ret) return NULL;

	pool->max = size < MP_SMALL_NODE_SIZE ? size : MP_SMALL_NODE_SIZE-1;
	pool->smalls_cur = pool->head;
	pool->larges = NULL;

	small = pool->smalls_cur;
	small->last = (unsigned char *)(small + 1);
	small->end = small->last + size;
	small->next = NULL;
	small->failed_count = 0;

	return pool;
}

3.2 内存池的销毁

内存池销毁需要先释放大块内存,再释放小块内存,主要是由于大块内存结构体是在小块内存中分配的。

int mp_pool_destory(mp_pool *pool)
{

	if(pool == NULL) return 0;

	mp_large_node *larges = pool->larges;
	while(larges)
	{
		if(larges->addr)
			free(larges->addr);
		larges = larges->next;
	}

	mp_small_node *small = pool->head->next;
	mp_small_node *tmp = NULL;
	while(small)
	{

		tmp = small->next;
		free(small);
		small = tmp;
	}

	free(pool);

	return 0;
}

3.3 内存分配

内存分配比较简单,根据大块内存和小块内存交给不同的函数处理。

unsigned char* mp_malloc(mp_pool *pool, size_t size)
{
	if(pool == NULL) return NULL;

	if(size <=  pool->max)
	{
		
		return mp_malloc_small(pool, size);
	}

	return mp_malloc_large(pool, size);
}

3.4 大块内存分配

大块内存分配主要是利用malloc分配,需要注意的是将大块内存的结构体在小块内存分配,方便后面统一回收。

unsigned char* mp_malloc_large(mp_pool *pool, size_t size)
{
	if(pool == NULL) return -1;

	unsigned char *addr = (unsigned char *)calloc(1, size);
	if(addr == NULL) return NULL;

	
	mp_large_node *larges = (mp_large_node *)mp_malloc_small(pool, sizeof(mp_large_node));
	if(larges == NULL) 
	{
		free(addr);
		return NULL;
	}
	
	larges->addr = addr;
	larges->next = NULL;

	mp_large_node *head = pool->larges;
	if(head == NULL) 
	{
		head = larges;
	}
	else
	{
		while(head->next)
		{
			head->next = head->next->next;
		}
		head->next = larges;
	}
	
	return larges->addr;
}

3.5 小块内存分配

小块内存分配分为两种情况一种是,内存块还有足够空间,直接分配出去,然后后移last指针即可。当不足分配时需要新创建节点,加入到链表中,并将内存返回。这里有个加速操作,利用当前指针指向分配位置的起点防止每次从头开始遍历。

unsigned char* mp_malloc_small(mp_pool *pool, size_t size)
{
	if(pool == NULL) return NULL;
	
	mp_small_node *cur = pool->smalls_cur;
	mp_small_node *node = NULL;
	unsigned char *tmp = NULL;
	while(cur)
	{
		tmp = mp_align_ptr(cur->last, MP_ALIGNMENT);
		if((cur->end - tmp) >= size)
		{
			cur->last = tmp + size;
			return tmp;
		}

		cur->failed_count++;

		cur = cur->next;
	}

	mp_small_node *new_node = NULL;
	size_t node_size = (size_t)(pool->head->end-(unsigned char *)pool->head);
	printf("size(%d)p-d(%d)\n", pool->max + sizeof(mp_small_node), node_size);
	int ret = posix_memalign((void **)&new_node, MP_ALIGNMENT, node_size);
	if(ret) return NULL;

	new_node->end = (unsigned char*)new_node + node_size;
	new_node->failed_count = 0;
	new_node->next = NULL;

	tmp += sizeof(mp_small_node);
	tmp = mp_align_ptr(tmp, MP_ALIGNMENT);
	new_node->last = tmp + size;

	cur = pool->smalls_cur;

	for (node = cur; node->next; node = node->next) 
	{
		if (node->failed_count > 3) 
		{
			cur = node->next;
		}
	}
	node->next = new_node;

	pool->smalls_cur = cur ? cur : new_node;

	return tmp;

}

3.6 内存的对其问题

在分配内存过程中,每次返回前需要将指针位置进行对齐操作,防止后面内存使用过程出问题。

#define mp_align_ptr(p, alignment) (void *)((((size_t)p) +(alignment-1)) & ~(alignment - 1))
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值