问题描述
某服务前端机器(nginx主机)125G内存,每三天左右,主机内存可用内存为0,重启nginx进程,可用内存又恢复正常,这种现象表明nginx进程存在内存泄漏。
临时措施
1、写定时脚本,每小时检查一次主机内存,小于5G时重启nginx进程
分析过程
1、 仔细认真分析阅读相关代码,考虑各种正常和异常情况,是否有程序退出时没有释放分配出来的内存
2、 借助其它工具进行检查内存泄漏点,这里使用的是valgrind ,具体使用如下
1) 用valgrind --tool=memcheck --leak-check=full --show-reachable=yes/usr/local/nginx/sbin/nginx 启动进程
2) 进行正常的业务逻辑请求测试
3) /usr/local/nginx/sbin/nginx -s stop停止进程
4) 查看valgrind返回的结果
==8395== LEAK SUMMARY:
==8395== definitely lost:8,264 bytes in 2 blocks. //确实存在内存泄漏
==8395== indirectly lost: 1,154,454 bytes in 229 blocks. //间接的存在内存泄漏
==8395== possibly lost:160 bytes in 1 blocks. //可能存在内存泄漏
==8395== still reachable:13,461,267 bytes in 3,059 blocks.
==8395== suppressed: 0bytes in 0 blocks.
5) 说明确实存在内存泄漏,进一步查看valgrind结果,发现内存泄漏点
==8395== 1,162,718 (8,264 direct,1,154,454 indirect) bytes in 2 blocks are definitely lost in loss record 11 of15
==8395== at 0x40053C0: malloc(vg_replace_malloc.c:149)
==8395== by 0x42EA6E3: AcquireMagickMemory(memory.c:464)
==8395== by 0x4113FFA: CloneMagickWandFromImages(magick-image.c:97)
==8395== by 0x80F7C53: dfs_image_resize(cfftn.c:1581)
6) 通过这些函数堆栈信息发现,使用第三方库ImageMagick进行图片压缩裁剪的时候存在内存泄漏,对比代码发现
magick_wand =MagickCoalesceImages(magick_wand); 比较可疑
7) 查看MagickCoalesceImages函数实现,发现这个函数内部最终会返回malloc的内存, MagickCoalesceImages malloc的值返回给magick_wand,而magick_wand原来指向的内存哪里去了?没有找到对应指针释放,这就是传说中内存泄漏。
解决方案
1、修改问题代码,返回值赋给新的变量,函数返回或结束时释放返回的内存指针
MagickWand *magick_wand_new = MagickCoalesceImages(magick_wand);
…..
If(magick_wand_new!=NULL)
{
DestroyMagickWand(magick_wand_new);
}
2、代码修改后用valgrind查看,没有发现内存泄漏
==8394== LEAK SUMMARY:
==8394== definitely lost: 0 bytes in 0 blocks.
==8394== possiblylost: 0 bytes in 0 blocks.
==8394== still reachable:34,030 bytes in 2,015 blocks.
==8394== suppressed: 0bytes in 0 blocks.
总结
1、 对于内存泄漏这种问题,要反复仔细的检查代码,考虑各种正常或异常情况下,返回时是否没有释放内存
2、 要善于借助工具来分析,排查问题
3、 在使用第三方库的时候,要仔细阅读接口的使用说明文档及返回值情况。最好能查看接口的实现
4、 在对外提供接口时,尽量做到谁分配内存,谁释放。不要在接口内分配内存,在接口外释放的这种情况。