应用程序的内存可以简单的氛围堆内存,栈内存;对于栈内存,函数编译的时候,编译器会移动栈到当前指针位置的代码,实现栈空间的管理。但是对于堆内存,通常需要程序员进行管理,我们常说的内存管理其实就是对堆空间的管理。
内存对齐
nginx的内存管理可以分为两个部分,一种是常规的内存池,也就是进程平时所需要的内存管理;一种是共享内存的管理。这里主要讲的是内存池管理内存
nginx的内存管理包括1三个部分:内存分配、内存对齐、内存释放
分配:nginx的内存分配本质上是对c语言的malloc进行封装,详情可以查看src/os/unix/ ngx_alloc.c
,其分配包括两种形式:
对齐:参考上文链接
释放:#define ngx_free free // 见os/unix/ ngx_alloc.h,宏定义。
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;
}
void *ngx_calloc(size_t size, ngx_log_t *log)
{
void *p;
p = ngx_alloc(size, log); //1.Nginx分配
if (p) {
ngx_memzero(p, size); //2.若分配成功,则Nginx初始化
}
return p;
}
nginx使用内存池来管理内存,当进程需要内存时,向内存池申请,使用后释放内存池。申请的内存主要分为两种:小块内存,大块内存
内存池释放的时候,可以调用回调清理函数,可以有多个回调函数,这些回调函数构成链表进行管理。
内存池的结构如下:一般来说,内存池的第一个结点会包含以下的数据结构,而从第二个结点开始,max——log部分会被缺省,当做空间进行分配。
其数据结构如下(详情查看:core/ngx_palloc.h):
typedef struct ngx_pool_s ngx_pool_t;
typedef struct {
u_char *last;//下一次分配的开始
u_char *end;//内存池结束的位置
ngx_pool_t *next;//内存池的内存块通过这个连成链表
ngx_uint_t failed;//内存池分配失败的次数
} ngx_pool_data_t;//d:数据块
struct ngx_pool_s {
ngx_pool_data_t d;//指向一个内存池的第一个数据块
size_t max;//判断大内存块和小内存块的标准
ngx_pool_t *current;//指向当前的内存池
ngx_chain_t *chain;//挂接一个ngx_chain_t结构
ngx_pool_large_t *large;//指向大块内存链表的第一个结点,即分配控件超过max的内存
ngx_pool_cleanup_t *cleanup;//指向清理函数的管理结点
ngx_log_t *log;//用来记录日志信息
};
struct ngx_pool_cleanup_s {
ngx_pool_cleanup_pt handler;//指向清理函数的地址
void *data;//data:用于向数据清理函数传递的参数,指向待清理的数据的地址,若没有则为NULL
ngx_pool_cleanup_t *next;//指向下一个清理函数的管理结点
};
//清理函数指针
typedef void (*ngx_pool_cleanup_pt)(void *data);
typedef struct ngx_pool_large_s ngx_pool_large_t;
struct ngx_pool_large_s {
ngx_pool_large_t *next;//指向分配的大块内存的下一个管理结点
void *alloc;//指向大块内存的地址
};
ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log)//创建内存池,size用来确定max
ngx_pool_t *p;
p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);//内存对齐
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->log = log;
return p;
{
//判别大小块内存方式
if (size <= pool->max)
{
return ngx_palloc_small(pool, size, 1);//对齐方式,申请小块内存,1表示对齐,0表示不对齐
}
return ngx_palloc_large(pool, size);
}
{
//判别大小块内存方式
If (size <= pool->max)
{
return ngx_palloc_small(pool, size, 0);//不对齐方式,申请小块内存
}
return ngx_palloc_large(pool, size);
}
{
void *p;
p = ngx_palloc(pool, size); //调用 ngx_palloc
if (p) //初始化处理
{
ngx_memzero(p, size);
}
return p;
}
static ngx_inline void *ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align){
do
{
m = p->d.last; //注意这个写法,d是p的非指针成员,last是d的指针成员
if (align) {
m = ngx_align_ptr(m, NGX_ALIGNMENT); //需要对齐,则对齐处理
}
if ((size_t) (p->d.end - m) >= size) //满足大小要求,划出分配区域
{
p->d.last = m + size; //划出size大小
return m;
}
p = p->d.next; //下一个内存块,遍历所有内存块
} while (p);
return ngx_palloc_block(pool, size);//申请新的内存块,完成该次小内存块申请
}
static void *ngx_palloc_large(ngx_pool_t *pool, size_t size)//大块
……
p = ngx_alloc(size, pool->log);//直接申请大内存
……//含有n=0语句
for (large = pool->large; large; large = large->next)//循环查找空闲large
{ //步骤1
if (large->alloc == NULL) //找到空闲large
{
large->alloc = p; //将大内存链入该空闲large
return p;
}
if (n++ > 3) { break; }//次数限制为4次?
}
large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
//申请新空白large,步骤2
if (large == NULL) { //失败,释放大内存,返回
ngx_free(p);
return NULL; }
large->alloc = p; //将大块内存链入large,步骤3
large->next = pool->large; //将新large链入内存池large链头,步骤4
pool->large = large;
ngx_int_tngx_pfree(ngx_pool_t *pool, void *p)
for (l = pool->large; l; l = l->next) //遍历内存池的large
{
if (p == l->alloc) //判定该large结点是否含有待释放大块内存
{ //调试信息
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l>alloc);
ngx_free(l->alloc); //释放该大块
l->alloc = NULL; //置该结点alloc为NULL
return NGX_OK;
}
}
voidngx_destroy_pool(ngx_pool_t *pool)
//先释放清理函数,因为需要先对内存进行有关处理
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); //调用一次该清理函数
}
}
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; }
}