Nginx 假性内存泄露
Nginx在使用中,发现其使用的VSZ
和RSS
都在不断变大,查看新增的代码,没有找到内存泄露的地方,ngx_palloc
和ngx_pfree
都是对称调用的。
Nginx 内存池机制
使用Nginx开发过的都知道,在Nginx里面普遍使用ngx_palloc
/ngx_pnalloc
申请内存,其中入参有一个pool
,它是从ngx_create_pool
获得的一个对象,该函数入参指定了这个内存池的大小。
后续是 ngx_palloc
/ngx_pnalloc
从pool中申请内存均在这块大内存中申请内存,内存的分配机制非常简单,线性搜索可使用的内存大小块即可。
Nginx 内存池机制中的坑
如果 pool中大小一共1k,然后使用完了,接着从这个pool中申请内存会怎么样?Nginx不会返回失败,而是
(1):如果申请的内存大小,小于1k,则通过memalign
申请一个1k pool2,挂在入参pool的链表后面,组成一个链表,然后接着从这个新pool2中申请内存。
(2):如果申请的内存大小,大于1k,则通过malloc
申请一个指定大小的内存,挂在入参pool中,注意这块内存不会当做pool用,这块内存简单的返回给调用者。
所有这一切内存,都可以在ngx_destroy_pool
中被释放,这是没问题的。但是,假设业务逻辑需要频繁申请内存,即频繁ngx_palloc
,担心内存泄漏的人肯定会主动调用ngx_pfree
希望立刻释放内存,但是实际上ngx_pfree
毫无效果。
ngx_pfree
只会释放(2)中的内存,而不会释放(1)中的内存。所以即使调用ngx_pfree
,也无法减少Nginx在调用ngx_palloc
中频繁增加的内存。
真碰到这种场景,只能使用ngx_alloc
和ngx_free
了,它们只是简单的封装了下glibc的malloc
和free
(当然glibc的free也有类似不释放的问题,只是比Nginx造成的问题小多了)。
或者在适当的时候调用ngx_reset_pool
,即重置内存池。注意ngx_reset_pool
只是重置,即把pool中内存的偏移全部重置,而不是释放内存,偏移重置后,这块内存又能被调用者申请到,而不会因为pool中内存不够而频繁的触发(1)或者(2)。