答案是内核态内存泄漏。free显示available持续下降而top中rss总和远小于used,差额大概率在slab、page cache、zram或驱动层;需优先检查sunreclaim、slabtop、zram状态及kmemleak等内核分配路径。

free 显示 available 持续下降,但 top 里 RSS 总和很小
这基本能锁定是内核态内存没释放,不是用户进程的问题。free 的 used 高、available 低,而 top 或 ps 算出来的所有进程 RSS 加起来远小于 used,差额部分大概率在 slab、page cache、zram 或驱动层。
优先检查:
- 运行 cat /proc/meminfo | grep -i "slab\|sreclaimable\|sunreclaim",看 SUnreclaim 是否随时间上涨
- 执行 slabtop -o(按 c 排序),关注 # use 高但 Obj/kB 极低的 cache(比如 kmalloc-192、size-32)
- 若系统启用了 zram,跑 cat /sys/block/zram0/mm_stat 看压缩内存是否占了大头
- buff/cache 涨得快但 available 不掉?大概率是业务读写密集,不是泄漏
pmap -x 发现 [anon] 区域持续膨胀
这是用户态程序堆内存没释放的典型信号。pmap 的 [anon] 行代表匿名映射(主要是 malloc/new 分配的堆),如果它的 Kbytes 列随时间单向增长,且不回落,说明程序有泄漏。
操作建议:
- 用 pmap -x <pid></pid> 抓 baseline,隔 5 分钟再跑一次,对比 RSS 和 [anon] 大小变化
- 如果发现多个小块 [anon](比如几百 KB 一堆),可能是高频小对象分配未释放
- 注意区分:heap 段膨胀 ≠ 泄漏,有些 GC 语言(如 Java)会预留但不立即归还,要看长期趋势
- 配合 cat /proc/<pid>/smaps_rollup</pid> 查 Anonymous 和 Heap 字段,比 pmap 更细粒度
valgrind 报 “definitely lost” 却找不到源码行号
常见于编译时没带调试信息,或用了高优化级(-O2/-O3)。valgrind 能检测到泄漏,但堆栈回溯为空或指向汇编,就失去定位价值。
必须满足的条件:
- 编译时加 -g,保留符号表
- 关闭优化:用 -O0 重新编译,避免内联和寄存器优化干扰调用栈
- 不要 strip 二进制:确认 file your_program 输出含 “not stripped”
- 若程序是动态链接的,确保 libc 也有调试符号(Ubuntu/Debian 可装 libc6-dbg)
内核模块疑似泄漏,但 slabtop 没明显异常
slabtop 看不出问题,不代表没有泄漏。某些内核分配路径(比如 per-CPU 内存、vmalloc 区、netdev 子系统)不走 SLAB,也不会出现在 /proc/slabinfo 里。
可尝试:
- 启用 kmemleak:确认内核配置了 CONFIG_DEBUG_KMEMLEAK,挂载 debugfs 后执行 echo scan > /sys/kernel/debug/kmemleak
- 用 perf record -e kmem:kmalloc,kmem:kfree -g -p <pid></pid> 捕获分配/释放事件,看是否有 kmalloc 多于 kfree 的调用链
- 检查 dmesg 是否有 “kmemleak:
- 特别注意:kmemleak 默认每 10 分钟扫描一次,手动触发后需等几秒再 cat /sys/kernel/debug/kmemleak
真正难缠的是那种缓慢、非线性的增长——比如每天涨 50MB,连续两周才触发 OOM。这种必须靠长期日志+趋势比对,单次命令基本无效。











