用户态泄漏用mtrace查malloc/free配对,内核态泄漏盯slabinfo和slabtop;虚拟内存分配具滞后性,需结合VIRT、RES、缺页中断综合判断。

先看是用户态还是内核态泄漏
这是最关键的分水岭。如果 free 显示可用内存持续下降,但 top 里所有用户进程的 RES 加起来并不高,那大概率是内核态泄漏——比如驱动、模块或 slab 分配器没释放内存。反之,若某个进程的 VIRT 膨胀到几十 GB 而 RES 增长缓慢(如 285g / 4.0g),说明它在疯狂 mmap 或 malloc 但未真正触碰物理页,属于用户态虚拟地址空间泄漏。
用户态泄漏:用 mtrace 快速验证 C 程序
mtrace 是 glibc 自带的轻量级工具,适合调试纯 C 写的后台服务,不需要重新编译整个项目,只要加两行代码就能启用:
- 在
main()开头调用mtrace(),结尾调用muntrace() - 运行前设置环境变量:
export MALLOC_TRACE=./mtrace.log - 运行后检查日志:未配对的
+行(只有 malloc 没 free)就是泄漏点,地址可配合addr2line -e ./your_binary 0x400634定位到源码行
注意:mtrace 不跟踪 new/delete(C++),也不捕获 mmap 或 brk 直接系统调用;且日志体积随分配次数线性增长,生产环境慎开。
内核态泄漏:盯死 /proc/slabinfo 和 slabtop
内核泄漏往往表现为 Slab 字段在 /proc/meminfo 中缓慢上涨,而 MemFree 持续缩水。这时别猜,直接看 slab 分配器的明细:
- 执行
cat /proc/slabinfo | head -20,找num_objs列长期增长、且名字和你模块相关的缓存项(如mydriver_cache、skb) - 用
slabtop -o实时排序,重点关注OBJS和CACHE SIZE列是否随时间单向爬升 - 配合
kmemleak(需内核开启CONFIG_KMEMLEAK)做扫描:echo scan > /sys/kernel/debug/kmemleak,再cat /sys/kernel/debug/kmemleak查未引用对象
常见陷阱:slabinfo 输出中 size-32 这类通用缓存暴涨,未必是你写的代码导致——可能是某个子系统(如 netfilter)高频创建小对象却未回收,得结合模块加载时间和内存增长节奏交叉判断。
别跳过虚拟内存映射滞后性这个底层事实
Linux 分配内存时只建页表、不给物理页,直到第一次写才触发缺页中断(lazy allocation)。所以 VIRT 高 ≠ 物理内存被占满,RES 低也不代表安全。排查时必须同时看:
-
pidstat -r -p $PID 1:观察%MEM(对应 RES)和minflt/majflt(缺页次数)是否同步飙升 -
pmap -x $PID | tail -5:看最大几块 mapping 的MMAP大小和RSS占比,区分是堆泄漏还是 mmap 泄漏 -
/proc/$PID/status里的VMData(堆)、VMStk(栈)、VMExe(代码段)字段,比top更细粒度
很多团队卡在“明明没 malloc 多少,VIRT 却爆了”,其实是因为用了 mmap(MAP_ANONYMOUS) 或 posix_memalign 分配大页,这些不会进 glibc heap,mtrace 完全看不见。








