Linux 内存泄漏排查常用方法

9次阅读

Linux 内存泄漏排查常用方法

Linux 内存泄漏排查不能只靠一个工具,得按“用户态程序”和“内核空间”分场景处理,再配合系统级观察和代码级验证。

用户态程序:用 valgrind 快速定位

valgrind 的 Memcheck 是最常用的内存泄漏检测器,适合 C/C++ 程序。它能捕获 malloc/free 不匹配、访问已释放内存等问题。

  • 编译时加 -g 保留调试信息,禁用优化(如 -O0),否则行号和调用栈可能不准
  • 运行命令示例:
    valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./myapp
  • 重点关注输出中的 definitely lost(确认泄漏)、indirectly lost(间接泄漏)和 possibly lost(疑似泄漏)
  • 若程序运行时间长、内存占用大,可搭配 massif 工具生成堆内存使用曲线:
    valgrind --tool=massif --time-unit=B ./myapp,再用 ms_print massif.out.xxxx 分析峰值与增长点

轻量高效替代:AddressSanitizer(ASan)

相比 valgrind,ASan 运行开销小(约慢 2 倍),集成在 GCC/Clang 中,更适合日常开发和 CI 流程。

  • 编译时加入:
    gcc -g -fsanitize=address -fno-omit-frame-pointer myapp.c -o myapp
  • 直接运行即可,一旦发生泄漏或越界,会打印带堆栈的详细错误报告
  • 支持内存泄漏检测(需链接 libasan 并启用 ASAN_OPTIONS=detect_leaks=1
  • 注意:ASan 无法检测未初始化读写(那是 UBSan 的范畴),但对泄漏本身足够可靠

系统级线索:从 /proc 和监控工具入手

当 top/ps 看不到明显高内存进程,但 free 显示可用内存持续下降,就要怀疑内核空间泄漏或隐式内存增长。

  • 查总体内存状态:cat /proc/meminfo | grep -E "(MemAvailable|Slab|SReclaimable|PageTables|KernelStack)",重点关注 SlabSReclaimable 是否异常增长
  • 盯住 slab 分配器:slabtop -o(按活跃对象排序),或 grep "size-4096" /proc/slabinfo 查特定大小缓存项是否持续增加
  • 对比用户进程 PSS:pmap -x <pid></pid>ps aux --sort=-%mem,排除误判为“无大进程”实则多个小进程累积占满的情况
  • 检查内核日志线索:dmesg -T | tail -50 | grep -i "memory|oom|alloc"

内核模块泄漏:kmemleak + trace-cmd 协同分析

内核空间泄漏不经过 glibc,valgrind 完全无效,必须依赖内核自带机制。

  • 确认内核已开启 kmemleak(CONFIG_DEBUG_KMEMLEAK=y),挂载 debugfs:
    mount -t debugfs nodev /sys/kernel/debug
  • 启动扫描并查看结果:
    echo scan=10 > /sys/kernel/debug/kmemleak
    cat /sys/kernel/debug/kmemleak
  • 若发现可疑地址,用 echo dump=0x…… > /sys/kernel/debug/kmemleak 查看分配上下文
  • 进一步精确定位:用 trace-cmd 跟踪 kmalloc/kfree 对应 size-4096 的调用:
    trace-cmd record -e kmem:kmalloc -e kmem:kfree --filter "bytes==4096" -T,再 trace-cmd report 比对申请与释放是否成对
text=ZqhQzanResources