赞
踩
内存池出现的意义比较重大,对于服务器这种需要长时间工作的设备内存池具有比较大的优势:一个是可以避免内存频繁的分配和释放,造成内存的碎片,久而久之内存分配失败。这里实现内存池比较简易主要是为了了解内存的管理策略,如果是商用建议采用开源框架ptmalloc、tcmalloc或者jemalloc等等。
内存池的管理,首先需要有大块内存和小块内存管理的问题,大块内存基本对于内存没有影响,因为本来就比较大不存在分配后出现内存碎片的问题,小块内存,则不一样每次分配和释放都会容易造成内存的碎片问题。所以讨论内存管理主要是对小块内存的管理,下面探讨内存管理的几种方案:
注意: 内存大小区分:没有标准,一般4K一下为小块内存,当然实际情况也可以调整。
每次分配将地址加入到链表中,释放的时候不去真正释放而是,将标志位置位0,供下一次分配。
存在问题:
按照固定大小进行分配,分别为32字节,64字节,128字节,256字节和512字节大小;所有配置利用将不足大小按照向上去近似进行分配,防止链表出现的小的碎片问题。
存在问题:
内存块管理采用的方式是进行将内存分配采用大块和小块两部分分别管理,大块比较简单,分配多少就多少,直接由内存malloc分配,释放也是直接free释放。对于小块内存管理则是采用一次性分配4k大小,一个指针指向分配的起始节点后面的起始位置,另一个指针指向节点的结束位置,中间分配内存时last指针跟随移动。
存在问题:
这里即采用的是按块进行内存管理的内存池方案,用于特定的连接生命周期这种局部性的内存池操作。
内存池初始化,首先分配第一块内存块,这里需要注意的是内存池对象和小块内存是连续内存,一般剩余空间按照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; }
内存池销毁需要先释放大块内存,再释放小块内存,主要是由于大块内存结构体是在小块内存中分配的。
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; }
内存分配比较简单,根据大块内存和小块内存交给不同的函数处理。
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);
}
大块内存分配主要是利用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; }
小块内存分配分为两种情况一种是,内存块还有足够空间,直接分配出去,然后后移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; }
在分配内存过程中,每次返回前需要将指针位置进行对齐操作,防止后面内存使用过程出问题。
#define mp_align_ptr(p, alignment) (void *)((((size_t)p) +(alignment-1)) & ~(alignment - 1))
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。