NGX_MAX_ALLOC_FROM_POOL
定义在 src\core\ngx_palloc.h
#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1)
在 src/os/unix/ngx_alloc.h
extern ngx_uint_t ngx_pagesize;
这个全局变量定义在
src\os\unix\ngx_alloc.c 中
ngx_uint_t ngx_pagesize;
在 src/os/unix/ngx_posix_init.c
ngx_int_t ngx_os_init(ngx_log_t *log) {ngx_time_t *tp;ngx_uint_t n; #if (NGX_HAVE_LEVEL1_DCACHE_LINESIZE)long size; #endif#if (NGX_HAVE_OS_SPECIFIC_INIT)if (ngx_os_specific_init(log) != NGX_OK) {return NGX_ERROR;} #endifif (ngx_init_setproctitle(log) != NGX_OK) {return NGX_ERROR;}ngx_pagesize = getpagesize();
这里被赋值
getpagesize
函数用于获取系统的内存页大小(page size)。内存页是操作系统管理内存的基本单位,通常以字节为单位。
不同的操作系统和硬件架构可能会有不同的页大小,常见的页大小有 4KB、8KB、16KB 等。
getpagesize
函数返回系统的页大小,单位为字节(bytes)为了使用
getpagesize
函数,你需要包含以下头文件:#include <unistd.h>
在第一次使用时(main 函数中首次使用 ngx_create_pool)
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
这里 还未执行 ngx_os_init 函数,ngx_pagesize 还未被赋值(初始值为0,64 位无符号整数 - 1 = 18446744073709551615)
所以这里 p->max 的值是 size 的值
NGX_MAX_ALLOC_FROM_POOL 为什么要设置为 ngx_pagesize - 1?
避免跨页分配
a. 内存页的概念
- 操作系统以页(page)为单位管理内存,常见的页大小为 4KB(4096 字节)或 8KB(8192 字节)。
- 分配内存时,操作系统通常会按页分配,即使只需要少量内存,也会分配整个页。
b. 跨页分配的问题
- 如果从内存池中分配的内存块大小接近或等于一页(如 4096 字节),可能会导致跨页分配。
- 跨页分配会增加内存管理的复杂性,并可能导致性能下降。例如:
- 缓存未命中率增加:数据分布在多个缓存行中,访问效率降低。
- 内存对齐问题:跨页分配可能导致未对齐访问,影响性能。
- 将最大分配大小限制为
ngx_pagesize - 1
,可以确保分配的内存块始终位于单个页内,避免跨页问题。- 例如,如果页大小为 4096 字节,则
NGX_MAX_ALLOC_FROM_POOL
的值为 4095 字节,确保分配的内存不会跨越页边界。
提高内存利用率
a. 小块内存分配
- Nginx 的内存池主要用于分配小块内存(如 HTTP 请求头、连接上下文等)。
- 这些小块内存的大小通常远小于
ngx_pagesize
,因此将最大分配大小限制为ngx_pagesize - 1
可以充分利用内存池的空间。b. 大块内存分配
- 如果需要分配的内存大小超过
ngx_pagesize - 1
,Nginx 会使用单独的大块内存分配机制(通过large
链表管理)。- 这种设计可以避免大块内存占用内存池空间,从而提高内存池的利用率。
简化内存管理
a. 内存池的设计目标
- 内存池的设计目标是高效地管理小块内存分配。
- 如果允许从内存池中分配过大的内存块,可能会增加内存池的复杂性。例如:
- 需要额外的链表或数据结构来跟踪大块内存的使用情况。
- 剩余的内存空间可能不足以满足其他分配请求,导致内存浪费。
b. 分离小块和大块内存
- 通过将最大分配大小限制为
ngx_pagesize - 1
,可以将小块内存和大块内存的管理逻辑分离:
- 小块内存直接从内存池中分配,管理简单高效。
- 大块内存通过单独的分配机制(如
malloc
或ngx_alloc
)进行管理,避免干扰内存池。