背景分析
nginx作为一个高效的服务器服务器框架,在网站搭建领域占领了很大比例;以成为不可忽视的一大块领域;它能如此高效的运行跟他的优秀的内存管理机制有很大的关系,今天抽出时间就来学习和分析下它优秀的内存管理机制。
代码分析
首先我们来看下nginx的内存池的初始化部分;
ngx_pool_t *
ngx_create_pool(size_t size)
{
ngx_pool_t *p;
p = ngx_memalign(NGX_POOL_ALIGNMENT, size);
if (p == NULL) {
return NULL;
}
p->d.last = (u_char *) p + sizeof(ngx_pool_t);
p->d.end = (u_char *) p + size;
p->d.next = NULL;
p->d.failed = 0;
size = size - sizeof(ngx_pool_t);
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
p->current = p;
//p->chain = NULL;
p->large = NULL;
//p->cleanup = NULL;
//p->log = log;
return p;
}
这是内存池初始化的部分,可以看到首先开辟了一块内存p->d.last 这个是内存池中可用内存的初始地址的地方,其中ngx_pool_t这个结构体是内存池里面的描述,内存池占用,不可以使用;这样就不难理解 p->d.last = (u_char*)p + sizeof(ngx_pool_t);这段代码了;再看下面p->d.end 指向的是内存池的结束的地方;p->d.next 这个变量是指向下一个内存块的,因为这个内存块没有使用,所以下一个内存块还没有开辟,自然为空;再看p->max 这个变量是描述这个内存块的可用内存大小;NGX_MAX_ALLOC_FROM_POOL这个弘一般是定义一个页的大小;
p->current 这里是指向当前使用的内存块的;p->large 这的变量是为了指向大内存管理这个地方的;
其中注释的地方现在不做了解,如果有兴趣自行百度;好了,到这里基本山nginx的内存池的初始化算是搞清楚了;下面看下内存的分配部分;
内存分配
先上代码:
void *ngx_palloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
if (size <= pool->max) {
return ngx_palloc_small(pool, size, 1);
}
#endif
return ngx_palloc_large(pool, size);
}
可以看到在分配内存的时候首先判断,需要分配内存的大小是否大于内存块的总大小,如多小于在内存池中分配,如果大于,则分配在大内存管理这边;
下面看看小内存的分配
static inline void *ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
{
u_char *m;
ngx_pool_t *p;
p = pool->current;
do
{
m = p->d.last;
if (align)
{
m = ngx_align_ptr(m, NGX_ALIGNMENT);
}
if ((size_t)(p->d.end-m)>=size)
{
p->d.last = m + size;
return m;
}
} while (p);
return ngx_palloc_block(pool, size);
}
在这里可以看出总的逻辑就是判断当前内存块是否足够分配这块内存,如果足够则分配,如果不够则开辟一块新的内存块然后分配;
大内存快的分配不做详解;后面会附上代码,如果有不理解的欢迎联系本人交流;留下本人QQ:820121952
好了,在这里大概的逻辑了解了,现在我们了解下内存池回收的处理
void ngx_destroy_pool(ngx_pool_t *pool)
{
ngx_pool_t *p, *n;
ngx_pool_large_t *l;
//ngx_pool_cleanup_t *c;
/*for (c = pool->cleanup; c; c = c->next) {
if (c->handler) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"run cleanup: %p", c);
c->handler(c->data);
}
}*/
#if (NGX_DEBUG)
{
/*
* we could allocate the pool->log from this pool
* so we cannot use this log while free()ing the pool
*/
for (l = pool->large; l; l = l->next) {
fprintf(stderr, "free: %p", l->alloc);
}
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
fprintf(stderr, "free: %p, unused: %zu", p, p->d.end - p->d.last);
if (n == NULL) {
break;
}
}
}
#endif
for (l = pool->large;l; l=l->next)
{
if (l->alloc)
{
ngx_free(l->alloc);
}
}
for (p=pool,n=pool->d.next;;p=n,n=n->d.next)
{
ngx_free(p);
if (n==NULL)
{
break;
}
}
}
额循环遍历释放了整个内存池;
总结:由此可见,此内存池的内存管理在分配不固定大小的内存使用的时候是非常高效的,然而如果在长时间内内存池得不到释放 的话将会吃尽内存;因此这个内存管理方式适合在一段时间就会清理一次内存的情况;不适用于长时间内存池得不到释放的情况。
附上代码:链接:https://pan.baidu.com/s/1MCUAnQcxDRTu9NVviFjShA 密码:npft