Nginx零内存碎片源于放弃运行时回收,将内存管理移至生命周期边界:主池线性分配、整块释放;大内存独立malloc并挂链表;三级池(全局/连接/请求)物理隔离、时间契约严格;越界使用或脱离pool管控即破坏零碎片。
Nginx 长周期运行下几乎不产生内存碎片,根本不是靠“回收做得好”,而是彻底放弃回收——把内存管理从运行时搬到了生命周期边界。看透这一点,就抓住了物理内存池设计的全部灵魂。
物理内存池不是分配器,是时间契约
ngx_pool_t 的主内存块(如默认 16KB)是一次 malloc 得到的连续物理页,它不拆解、不合并、不复用单个字节。整个块只做两件事:
last 指针线性推进,分配小块(≤4095 字节),O(1),无元数据、无锁、无对齐浪费(仅按 16 字节对齐,为 cache 友好) 这块内存的生命周期 = 一次请求的存活时间。它不跨请求、不跨连接、不跨进程。物理上,它就是一段被标记为“已使用”的虚拟内存区域,直到销毁那一刻才还给操作系统。中间没有“释放”动作,自然没有碎片生成条件。
大块内存独立隔离,不污染主池物理连续性
超过阈值的大内存(如上传文件缓冲区、长响应体)不进入主池线性区,而是单独调用 malloc 分配,并以 ngx_pool_large_t 结构挂入链表。这个结构只存 next 和 alloc 地址,开销极小。
关键在于:这些大块内存与主池物理地址完全无关,既不会打断主池的连续布局,也不会因大小不定导致内部碎片。销毁时统一遍历链表 free,逻辑清晰,无依赖。
三级物理归属,杜绝跨生命周期混用
三者物理内存各自独立、互不嵌套(子池是新 malloc 块,非主池切分),边界绝对清晰。一个本该属于连接池的 SSL session key,绝不会误入请求池——否则每请求都重建一次,物理内存就白白多 alloc/free 数十次。
真正破坏零碎片的,永远是“越界”而非“分配”
只要出现以下任一行为,物理内存池的零碎片保障立刻失效:
这些都不是 ngx_pool_t 的缺陷,而是违背了它所依赖的“物理内存 + 时间契约”这一底层约定。
不复杂但容易忽略