Linux内存泄漏定位:从现象到进程的排查步骤
服务器内存一直涨,重启后又慢慢升高,多数情况下不是系统本身的问题,而是某个进程没有正确释放内存。
定位思路很简单:先确认内存到底被谁用掉,再观察它是否持续增长,最后锁定具体进程和代码路径。
下面按这个顺序展开。
先判断是缓存还是真实占用
登录服务器后不要急着下结论,先看整体内存分布:
free -h
重点看 available 这一列。
Linux 会把空闲内存拿去做文件缓存,buff/cache 偏高但 available 充足,通常不是泄漏。
真正需要警惕的是 available 持续下降,同时 used 不断上升。
为了排除缓存干扰,可以手动释放缓存观察变化(生产环境谨慎操作):
echo 1 > /proc/sys/vm/drop_caches
free -h
如果释放缓存后可用内存明显回升,说明之前大部分是缓存;
如果释放后很快又降下去,问题更可能出在进程上。
用 top 找到内存消耗大户
确认是进程问题后,用 top 按内存排序:
top
进入界面后按 Shift + M,进程会按内存占用从高到低排列。
重点看 RES 列,它表示进程实际占用的物理内存。
零基础用户容易混淆两列:
VIRT:虚拟内存,包含申请但未真正使用的部分,参考价值有限。RES:常驻内存,才是真实占用的物理内存。
如果某个进程的 RES 明显高于其他进程,先记下它的 PID,继续观察。
观察内存是否持续增长
单次快照不能证明泄漏,需要看趋势。
用循环命令每隔一段时间记录一次:
while true; do date; ps -p 1234 -o pid,rss,vsz,comm; sleep 60; done
把 1234 换成实际 PID。RSS 单位是 KB,如果它在一段时间内只涨不降,并且业务量没有同步增加,就基本可以怀疑泄漏。
也可以借助 smem 查看更细的 PSS 数据:
smem -k -P 进程名
PSS 会把共享内存按比例分摊,比 RSS 更接近真实占用。
深入进程内部看内存分布
锁定进程后,用 pmap 查看它的内存段:
pmap -x 1234 | sort -k3 -n -r | head -20
输出里重点看 mapped 和 dirty 较大的匿名段。
如果某段匿名内存持续增大,往往对应堆内存或 mmap 分配没有释放。
还可以直接读 /proc 目录:
cat /proc/1234/status | grep -i vm
cat /proc/1234/smaps_rollup
VmRSS 对应常驻内存,VmSize 对应虚拟内存。smaps_rollup 里能看到 Anonymous 和 Private_Dirty 的汇总,方便快速判断。
结合日志和监控确认根因
内存增长的常见来源包括:
- 程序内部缓存没有设置上限。
- 连接、文件句柄或对象没有正确释放。
- 定时任务或请求处理中反复创建大对象。
可以检查应用日志中是否有频繁重连、队列堆积、超时重试等异常。
如果服务器装了监控工具,调出该进程最近几天的内存曲线,和业务高峰对比,更容易判断是正常波动还是泄漏。
对于 PHP、Java、Python 这类语言,还可以用各自的内存分析工具进一步定位,例如 Java 的 jmap、PHP 的 memory_get_usage 等。
避坑与验证
排查过程中注意几点:
- 不要只看
free就判断泄漏,buff/cache高是正常现象。 - 不要用
VIRT排序找问题进程,容易误判。 - 释放缓存和重启服务都会影响线上业务,操作前确认影响范围并做好回滚准备。
- 单次采样没有意义,至少观察 10 到 30 分钟。
处理完成后,继续用同样的 ps 循环观察一段时间。
如果 RSS 稳定不再持续上涨,说明处理有效;
如果仍然增长,需要回到 pmap 和日志环节继续缩小范围。
内存泄漏定位没有一键命令,核心就是先看整体、再锁进程、后看内部结构,最后用趋势验证。
按这个顺序走,零基础也能一步步把范围缩小到具体进程。