内存泄漏并不是“爆炸性内存飙升”,而是程序申请了内存但没有释放,造成系统可用内存逐渐减少,直到用光。
表现形式:
- 系统空闲内存越来越少;
- swap频繁被占用;
- 某些服务响应变慢甚至挂掉;
- 重启服务后内存才释放。
内存泄漏的根源在哪里?
应用代码问题:编写程序时未释放 malloc/new 等分配的内存
守护进程缺陷:nginx、php-fpm、java 等长期运行服务的bug
长时间运行的脚本:python、node、shell等后台脚本未释放变量
第三方库问题:依赖库内部泄漏,难以直接发现
系统内核模块或驱动:较罕见,可能是内核升级或驱动兼容性问题
如何排查内存泄漏问题?(最关键的一步)
第1步:查看系统内存使用情况
free -m
重点观察:
available 是否长期趋近 0;
swap 是否大量使用;
buffers/cache 是否偏高。
第2步:定位占用内存的“元凶进程”
top -o %MEM
或使用更强大的命令:
ps aux --sort=-%mem | head -n 10
输出示例:
USER PID %MEM COMMAND
root 2145 42.3 java
www 3172 23.5 php-fpm
这时要重点盯住那些常驻内存、占比异常的进程。
第3步:深入分析进程内存使用情况
3.1 smem 工具(安装后用法超简洁)
yum install -y smem
smem -r -k
它能看到真实使用的物理内存(不被 cache 虚化)。
3.2 pmap 工具(进程内存映射)
pmap -x | sort -k3 -n | tail -n 20
查看哪个模块(库/文件)占了大量内存。
第4步:使用 valgrind 定位代码层内存泄漏(适合C/C++程序)
valgrind --leak-check=full ./your_program
输出示例:
==1234== 40 bytes in 2 blocks are definitely lost in loss record 1 of 1
==1234== at 0x4C2FB55: malloc (vg_replace_malloc.c:299)
可以精准告诉你哪一行没有释放内存。
第5步:检查日志和历史增长趋势
长期运行的服务可能出现“缓慢型内存泄漏”,可使用:
sar -r -f /var/log/sa/saXX
查看历史内存使用情况。saXX 是日期编号。
如何解决内存泄漏问题?
方法1:重启相关服务,释放内存(临时措施)
systemctl restart php-fpm
systemctl restart nginx
systemctl restart your_custom_app
这属于“治标不治本”,适用于紧急止血。
方法2:升级服务组件或更换依赖库
举例:
PHP-fpm 内存泄漏,尝试升级到更新的版本;
Java 程序泄漏,尝试升级 JDK 版本或 GC 参数优化;
nginx 模块引起的,尝试重新编译或精简模块。
方法3:限制内存占用,防止“拖垮系统”
使用 systemd 中的内存限制功能:
编辑服务配置:
[Service]
MemoryLimit=512M
然后:
systemctl daemon-reexec
systemctl restart your-service
或者使用 ulimit 限制:
ulimit -v 524288 # 最大虚拟内存限制
方法4:定期监控 + 脚本自动检测
编写脚本检测进程内存暴涨,超过阈值就重启服务:
#!/bin/bash
PID=$(pidof php-fpm)
MEM=$(pmap $PID | tail -n 1 | awk '/[0-9]K/{print $2}' | sed 's/K//')
if [ "$MEM" -gt 1048576 ]; then
echo "php-fpm memory leak suspected, restarting..."
systemctl restart php-fpm
fi
可写入 crontab 每小时执行一次。
结语:内存泄漏不可怕,排查有套路。内存泄漏是“慢性病”,不会一夜爆炸,却能一点点拖垮系统。掌握“发现 → 定位 → 优化”三步法能帮助你“提前防火”