Nginx内存池实现以及分析
开源案例
- tcmalloc
- jemalloc
- nginx
为什么需要内存池
- 为了解决在高并发下,需要不断申请内存和效率,造成了性能消耗,且造成了内存碎片化,不易统一管理。
内存池结构
struct mp_large_node
{
struct mp_large_node *next; //链表
void *data; // 指向大块存储空间的指针
};
struct mp_small_node
{
struct mp_small_node *next; //指向下一个small_node
unsigned char *last; //数据存储区起始的地址
unsigned char *end; //数据存储区最后的地址
size_t failed; //该块分配内存失败的次数
};
struct mp_pool
{
size_t max;
struct mp_small_node *current; // small_node 链表
struct mp_large_node *large; // large_node 链表
struct mp_small_node head[0]; //柔性数组 实现可变结构体
};
图解
上图mp_node_s即为mp_small_node
- pool指向一个包含了mp_pool,mp_small_node以及data大小和的内存空间。
- small_node 指向一个包含了small_node以及data大小和的内存空间,通过next字段不断连接。
- large_node 包含指向下一个large_node指针和指向具体数据的指针.
nginx内存池设计
- 当我们分配较小内存片段时,通过small_node 管理。
- 当我们分配较大内存片段室,通过large_node 管理。
- 做到了内存复用,统一管理,不需要不断申请和销毁内存。
- 内存池中分配的内存,最后统一销毁。
nginx内存池分配内存流程
内存池创建
struct mp_pool *mp_pool_create(size_t size)
{
struct mp_pool *pool;
int ret = posix_memalign((void **)&pool, MP_ALIGNMENT, size + sizeof(struct mp_small_node) + sizeof(struct mp_pool));
if (ret)
{
printf("create error\n");
return NULL;
}
pool->max = (size < MP_MAX_ALLOC_FROM_POOL) ? size : MP_MAX_ALLOC_FROM_POOL;
pool->large = NULL;
pool->current = pool->head;
pool->head->last = (unsigned char *)pool + sizeof(struct mp_small_node) + sizeof(struct mp_pool);// 内存池中第一个内存段包含了pool+smallnode+data的大小
pool->head->end = pool->head->last + size;
pool->head->failed = 0;
return pool;
}
内存池销毁
void mp_pool_destroy(struct mp_pool *pool)
{
struct mp_large_node *large = pool->large;
struct mp_small_node *nxt, *node;
for (large = pool->large; large; large->next) //销毁所有large_data指向的数据
{
if (large->data)
{
free(large->data);
}
}
node = pool->head->next;
while (node) //销毁 所有small_node
{
nxt = node->next;
free(node);
node = nxt;
}
free(pool); //销毁内存池,pool指向了pool+small_node_data的数据大小
}
内存池分配内存
void *mp_malloc(struct mp_pool *pool, size_t size)
{
if (size <= pool->max) //属于小内存
{
struct mp_small_node *n = pool->current;
while (n)
{
if ((size_t)(n->end - n->last) >= size) //在其中一个小的内存片段中能够塞下需要的新内存大小
{
void *pos = n->last;
n->last += size;
return pos;
}
n = n->next;
}
return mp_malloc_small(pool, size); // 现有内存片段不够存储,搞一个新的内存片段
}
return mp_malloc_large(pool, size); //分配大内存
}
创建small_node
static void *mp_malloc_small(struct mp_pool *pool, size_t size)
{
unsigned char *m;
struct mp_small_node *head = pool->head; //pool中small_node的起始地址
size_t psize = (size_t)(head->end - (unsigned char *)head);
int ret = posix_memalign((void **)&m, MP_ALIGNMENT, psize); //分配一个small_node 大小
if (ret)
{
return NULL;
}
struct mp_small_node *newnode, *current, *p;
newnode = (struct mp_small_node *)m;
current = pool->current;
p = current;
while (p->next)
{
p = p->next;
}//找到最后一个small_node
p->next = newnode;
newnode->next = NULL;
newnode->failed = 0;
newnode->last = m + size;
newnode->end = m + psize;
return m;
}
创建large_node
static void *mp_malloc_large(struct mp_pool *pool, size_t size)
{
void *data = malloc(size);
if (data == NULL)
return NULL;
struct mp_large_node *large = pool->large;
while (large)
{
if (large->data == NULL) //如果指针为空,则指向新分配的内存空间
{
large->data = data;
return data;
}
}
struct mp_large_node *newnode = mp_malloc(pool, sizeof(struct mp_large_node)); //如果所有large_node 都占用了 就分配一新的
if (newnode == NULL)
{
free(data);
return NULL;
}
newnode->data = data;
newnode->next = pool->large;
pool->large = newnode; //头插法插入
return data;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。