引言:
C/C++下内存管理是让几乎每一个程序员头疼的问题,分配足够的内存、追踪内存的分配、在不需要的时候释放内存——这个任务相当复杂。而直接使用系统调用malloc/free、new/delete进行内存分配和释放,有以下弊端:
- 调用malloc/new,系统需要根据“最先匹配”、“最优匹配”或其他算法在内存空闲块表中查找一块空闲内存,调用free/delete,系统可能需要合并空闲内存块,这些会产生额外开销
- 频繁使用时会产生大量内存碎片,从而降低程序运行效率
- 容易造成内存泄漏
内存池(memory pool)是代替直接调用malloc/free、new/delete进行内存管理的常用方法,当我们申请内存空间时,首先到我们的内存池中查找合适的内存块,而不是直接向操作系统申请,优势在于:
- 比malloc/free进行内存申请/释放的方式快
- 不会产生或很少产生堆碎片
- 可避免内存泄漏
因此,池化的思想广泛应用于服务器的设计,不管是Apache服务器还是Nginx服务器。今天我们就一起研究一下以精巧著称的nginx的内存池。Nginx内存池的定义在src/core/ngx_palloc.h|c中,当前博客使用版本为 Nginx-1.11.13。
1.Nginx内存池——数据结构定义:
ngx_pool_data_t:内存池数据块结构
typedef struct {
u_char *last;
u_char *end;
ngx_pool_t *next;
ngx_uint_t failed;
} ngx_pool_data_t;
- last: 是一个unsigned char 类型的指针,保存的是当前内存池已分配内存的末位地址,即下一次分配从此处开始。
- end: 是内存池的结束位置。
- next:内存池里面有很多块内存,这些内存块就是通过该指针连成链表的,next指向下一块内存。
- failed: 内存池分配失败次数。失败次数多的话,分配内存就会忽略该内存块。
ngx_pool_s:内存池头部结构
struct ngx_pool_s {
ngx_pool_data_t d;
size_t max;
ngx_pool_t *current;
ngx_chain_t *chain;
ngx_pool_large_t *large;
ngx_pool_cleanup_t *cleanup;
ngx_log_t *log;
};
- d:内存池数据块结构
- max:当内存的大小小于等于max字段的时候,则分配新的内存块,链接在d这个字段(实际上是d.next字段)管理的一条链表上。当要分配的内存块是比max大的,那么从系统中申请的内存是被挂接在large字段管理的一条链表上。我们暂且把这个称之为大块内存链和小块内存链。当一个ngx_pool_t对象被创建以后,该对象的max字段被赋值为size-sizeof(ngx_pool_t)和NGX_MAX_ALLOC_FROM_POOL这两者中比较小的。
- current:指向当前内存池,这个current初始指向第一个内存块,但是如果当前内存块failed过大,会指向next的内存块。
- chain:该指针挂接一个ngx_chain_t结构,这是个内存缓冲区链,具体会在ngx_chain_t中介绍。
- large:大块内存链表,即分配空间超过max的情况使用;
- cleanup:管理着一个特殊的链表,该链表的每一项都记录着一个特殊的需要释放的资源以及释放内存池的call_back。
另外,网上资料还有一张经典的图,如下所示。介绍的也很清晰,大内存块小内存块都有表明,但是有一点问题在于:通过源码的阅读我发现只有第一块内存块是有ngx_pool_t的完整结构的,后续的内存块是没有除ngx_pool_data_t之外的结构。也可能是我理解问题,如有疑问,请留言。
1.Nginx内存池—相关函数介绍:
在分析内存池方法前,需要对几个主要的内存相关函数作一下介绍,这些函数定义在src/os/unix/ngx_alloc.h|c中。
ngx_alloc:(只是对malloc进行了简单的封装)
void *
ngx_alloc(size_t size, ngx_log_t *log)
{
void *p;
p = malloc(size);
if (p == NULL) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
"malloc(%uz) failed", size);
}
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size);
return p;
}
ngx_calloc:(调用了ngx_alloc,只是比它多了初始化为0的操作)
void *
ngx_calloc(size_t size, ngx_log_t *log)
{
void *p;
p = ngx_alloc(size, log);
if (p) {
/* 初始化为0*/
ngx_memzero(p, size);
}
return p;
}
ngx_memzero:(初始化为0操作)
声明:Niginx学习,参考了 http://blog.csdn.net/column/details/niginxsourcelearning.html 专栏 以及 http://tengine.taobao.org/book/ 相关内容,主要是作为个人的学习笔记,加强记忆。如转载,请标明出处。