nginx reload内存碎片问题
相关资料
jemalloc、pmap、redis、systemtap、内存紧缩
问题现象
cache nginx 开启ip 库功能 占用700M 内存,reload 后内存翻倍,无法释放
root 9186 0.2 7.2 2150680 1786272 nginx master
reload 前 800M reload 后1.7G
根据描述有free,且多次reload 后内存不再增长;ip库使用系统malloc 和 free 没有使用内存池
问题原因
ip 库malloc 和free 大量紧邻操作可能导致glibc 内存管理合并机制没有归还给操作系统;解决方案使用malloc_trim() 函数归还内存给系统。
排查过程
首先验证是否存在内存泄漏
-
使用systemtap 脚本 排查没有存在内存泄漏,NGINX 确实有释放
-
使用pmap PID查看进程占用内存
000000000017a0000 872628K rw---- [ anon ]
xxxxxxxxxxxxxxxxx [ zero deleted ]reload 后, pmap 查看 从系统角度而言原先分配的内存确实还占用着,且又新分配了一段空间
结合ipku的频繁内存malloc 和 free 极有可能是内存碎片
000000000017a0000 872628K rw---- [ anon]
xxxxxxxxxxxxxxxxx xx xx [ zero deleted ]
00007fa033f930000 782890K rw---- [ anon]
xxxxxxxxxxxxxxxxx xx xx [ zero deleted ]
- 使用glibc 提供的malloc_trim 函数进行内存紧缩,内存得到释放。malloc_trim 是 GLIBC 4.0 以上提供的接口,紧缩为阻塞操作,可能影响服务时间,建议动态接口触发
扩展
C 等语言内存碎片如果不用内存池,会有碎片率问题:
- 如何计算内存碎片率
redis 是如何计算内存碎片率没有深入研究过。但是像squid 类似的软件,对malloc 有做一层封装xmalloc,那么只需要统计(xmalloc-xfree)获得squid 内部的分配数 除以 jemalloc 的malloc static分配数 即可得到底层系统库内存碎片率,前提要求只有squid 才用到了jemalloc 采用静态库接口yes_jemalloc 而其他so 用的是glibc,算起来会比较准确 - 深入研究
redis 有内存碎片查看接口,redis4 支持内存碎片清理、redis5 出了主动碎片整理V2 更好的内存统计报告、jemalloc5.1