Nginx内存池:内存分配方案 | 小块内存分配方案
2023-09-27 14:29:25 时间
内存分配函数
内存分配时考虑了小块和大块,用不同函数实现,
如果申请的空间大小 <= 当前内存池的max,调用ngx_palloc_small();
否则调用 ngx_palloc_large()
1. 考虑内存对齐版本
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);
}
2. 不考虑内存对齐版本
void *ngx_pnalloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
if (size <= pool->max) {
return ngx_palloc_small(pool, size, 0);
}
#endif
return ngx_palloc_large(pool, size);
}
3. 初始化为0的版本
在ngx_pcalloc()函数中,依然是调用 ngx_palloc(),但会将申请的空间全部置为0.
void *ngx_pcalloc(ngx_pool_t *pool, size_t size)
{
void *p;
p = ngx_palloc(pool, size);
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)
{
u_char *m;
ngx_pool_t *p;
p = pool->current; // 1
do {
m = p->d.last; // 2
if (align) { // 3
m = ngx_align_ptr(m, NGX_ALIGNMENT);
}
if ((size_t) (p->d.end - m) >= size) { // 4
p->d.last = m + size;
return m;
}
p = p->d.next; // 5
} while (p);
return ngx_palloc_block(pool, size);
}
static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new;
psize = (size_t) (pool->d.end - (u_char *) pool); // 6
m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log); // 7
if (m == NULL) {
return NULL;
}
new = (ngx_pool_t *) m; // 8
new->d.end = m + psize; // 9
new->d.next = NULL;
new->d.failed = 0;
m += sizeof(ngx_pool_data_t); // 10
m = ngx_align_ptr(m, NGX_ALIGNMENT); // 11
new->d.last = m + size; // 12
// 13
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; // 14
return m;
}
- 进入函数后先申请两个指针,一个是无符号char类型 m,一个是内存池类型的 p,先将p指向当前内存池pool的current,也就是内存池的开头:如图中p。
- 进入do…while()循环,意味着不管循环条件是否满足,都要执行一遍do的语句,第2步让指针 m 指向 p的last,也就是内存池可分配内存的起始位置。
- 判断参数align ,如果为真,需要进行内存对齐处理,否则不进行。
- 判断当前内存池可用空间是否大于等于 需要申请的空间size,如果满足条件,那么直接将last指针偏移 size个字节,返回 指针 m 即可。
- 如果上面的步骤没有返回,就让p指向下一个小块的内存池,但初始化时候next域设置成了NULL,所以循环退出了,需要执行下面的ngx_palloc_block()函数。
- 进入该函数,先计算内存池pool的总大小,赋值给 psize;
- 再次申请一个内存池,大小是 psize,并由 m指向该内存池起始位置。
- 让new指针指向m的位置。
- 将这一块内存池的数据域初始化(end指向末尾,next置为NULL,failed置为0)。
- m指向该内存池可供使用的起始位置(偏移一个ngx_pool_data_t的字节个数)。
- 将m调节至指定的倍数:宏定义如下
#define ngx_align_ptr(p, a) \
(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
- 将last指针调节至 m + size的位置,因为这块内存就是要分配出去的。
- 该for()循环是从第一个内存池开始检查,检测 failed标记加一后是否大于4,如果大于4,直接让current指针指向下一个内存池的起始位置,意味着以后都不在这个failed > 4 的内存池申请空间了,因为只剩一点点空间或者不剩空间了。
- 让指针 p 的next域指向 new,即和下一块内存池连接起来。
- return m,由于last指针指向了 m + size的位置,那意味着返回的m 到 m + size这块空间就是客户需要的那一块内存。
相关文章
- Win64 驱动内核编程-3.内核里使用内存
- Lua代码内存泄漏的疑惑
- Linux Command vmstat 监控内存
- Linux 内存中的 Cache 真的能被回收么?
- ES 内存管理分析
- ubuntu查看内存占用和查看cpu使用情况的简单方法(ubuntu内存管理)
- Linux下查看某一进程所占用内存的方法
- 物理内存,虚拟内存,进程地址空间
- 并发编程之 Java 内存模型 + volatile 关键字 + Happen-Before 规则
- PostgreSQL内存使用增长观察
- JAVA 是否会发生内存泄露(转)
- Linux环境下多线程C/C++程序的内存问题诊断
- 闪存、内存涨价贡献大:美光2017财年第二季度营收暴涨58%
- C和C++内存分配方式记录