在Nginx中,小块内存通常指的是那些大小相对较小、分配和释放频率较高的内存块。这些内存块由于数量众多、管理复杂,因此需要使用一种高效的内存管理机制来减少内存管理的开销和内存碎片的产生。
Nginx内存池通过一种预分配和复用的方式来管理小块内存。当需要分配小块内存时,内存池会首先检查是否已经有可用的内存块。如果有,则直接从中分配一个;如果没有,则根据预设的规则进行内存块的预分配。这种预分配的策略可以确保在需要时能够快速获取到内存块,避免了频繁的malloc和free操作带来的开销。
ngx_palloc_small函数
分配小块内存空间
static ngx_inline void *
ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align) // align表示是否考虑内存对齐
{
u_char *m;
ngx_pool_t *p;
p = pool->current; // 每次都从内存池中current块进行分配内存
do {
m = p->d.last; // 可分配内存的起始地址
if (align) {
m = ngx_align_ptr(m, NGX_ALIGNMENT); // 将m调整到 平台相关的ulong 的整数倍
}
if ((size_t) (p->d.end - m) >= size) { // 内存池空闲内存空间 >= 申请的内存空间
p->d.last = m + size; // 向下偏移到新的可分配起始地址
return m;
}
p = p->d.next; // 本块剩余空闲内存不够,则向后(小块内存池链上的下一块)寻找
} while (p);
// 找到末尾发现当前内存块链表的空闲内存不够用,则调用函数新分配一个内存块
return ngx_palloc_block(pool, size);
}
当前剩余空间够用的情况下,直接分配空闲内存的示意图:
ngx_palloc_block函数
当前剩余空间不够用的时候,开辟已一个新的内存块添加到(链接)内存池中,供小内存分配使用。
static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new;
// 计算pool的带头信息的已使用内存的大小
psize = (size_t) (pool->d.end - (u_char *) pool);
// 开辟内存对齐psize大小的内存块
m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
if (m == NULL) {
return NULL;
}
new = (ngx_pool_t *) m; // new指向新开辟块的起始地址
new->d.end = m + psize; // 指向新块的末尾地址
new->d.next = NULL;
new->d.failed = 0;
// m指向每块头信息ngx_pool_data_t之后的可用起始地址
m += sizeof(ngx_pool_data_t);
// 调整对齐倍数,准备将m分配出去
m = ngx_align_ptr(m, NGX_ALIGNMENT);
new->d.last = m + size; // 分配出size,指向新空闲地址
// 对已有的成链的各块,若从current块开始分配内存失败多于4次
// 说明current块的剩余可用内存很小了,认为很难再分配出去
// 就将下一个块作为current块
for (p = pool->current; p->d.next; p = p->d.next) {
if (p->d.failed++ > 4) {
pool->current = p->d.next;
}
}
p->d.next = new; // 将内存块成链(尾插法)
return m; // 返回新分配的给用户的地址
}
调用ngx_palloc_block函数分配新的内存块示意图:
总结——没有释放和回收逻辑
值得注意的是,Nginx内存池没有提供小块内存的释放和回收的任何逻辑。这是由Nginx的工作环境所决定的。
nginx本质是http服务器,是一个短链接的服务器,客户端(浏览器)发起一个request请求,到达nginx服务器以后,处理完成,nginx给客户端返回一个response响应,http服务器就主动断开tcp连接。即使http 1.1 keep-avlie可以保持60s,但也只是60s。http服务器(nginx)返回响应以后,需要等待60s,60s之内客户端又发来请求,重置这个时间,否则60s之内没有客户端发来的响应,nginx就主动断开连接,此时nginx可以调用ngx_reset_pool重置内存池了,等待下一次该客户端的请求。
所以没有小内存的释放逻辑也是合乎其工作方式,因为每个内存池在一段时间内一定会被reset成空内存池,加上小内存释放逻辑反而可能会降低服务器性能。