linux内存持续上涨未必是泄漏,但若available单边下降且不随负载回落,需排查;重点看rssanon、heap、mmap_area是否单调增长,结合slabtop、valgrind等工具定位根因。

Linux内存持续上涨不等于一定发生了内存泄漏,但若上涨不可逆、不随负载下降而回落,就该严肃排查。真正要盯的不是“用了多少”,而是“谁占着不放、为什么放不掉”。
先看系统级表征:free 和 vmstat 快速筛异常
运行 watch -n 5 'free -h',重点观察两列:
- available:持续单边下降,且不随业务低谷回升 → 内存被“钉住”,回收机制失效;
- buff/cache:同步上涨是正常缓存行为;但若它涨得猛、available 却跌得更快,尤其 SReclaimable(可回收 slab)不增,Slab 或 SUnreclaim 却持续走高 → 很可能是内核态泄漏(如 dentry、inode、skbuff 等对象未释放)。
再跑 vmstat 1,关注:
- sr(或 re)> 0 且稳定存在:kswapd 持续扫描 LRU 链表,说明物理页压力大;
- si/so 非零:已开始 swap,是内存严重不足的强信号;
- in(中断)或 cs(上下文切换)突增 + available 下跌:可能指向高频内核分配路径,比如网络收包、定时器回调反复 kmalloc。
定位用户进程:别信 top 的 RES,盯死 /proc/[pid]/smaps
top 或 ps 显示的 RES 包含共享内存、mmap 文件页、匿名页等,初期泄漏时变化迟钝。真实泄漏信号藏在:
- RssAnon:堆外匿名页(如 mmap(MAP_ANONYMOUS)、libcurl/openssl 的内部缓冲区);
- heap:malloc/sbrk 分配的堆内存;
- MMAP_AREA(如 libcurl 常用区域):容易被忽略,但泄漏频发。
快速抓取示例:
awk '/^RssAnon:|heap:|^MMAP_AREA/ {print}' /proc/1234/smaps
建议用循环每 5 秒采样一次,观察是否单调增长。若 RES 突增但 RssAnon 不变,大概率是 page cache 或共享内存,不是泄漏。
进程内部分析:线上不能停?用 gdb 实时看堆状态
对无法重启的服务,gdb 是最轻量的实时诊断工具:
- gdb -p [pid] 进入后执行 call malloc_stats():看 “total allocated” 是否持续上涨,“fastbins/unsorted bins” 是否长期积压;
- 若 “max system bytes” 增速明显快于 “total allocated”,说明 malloc 正不断向内核申请新页,泄漏已较严重;
- 加载 gef 或 pwndbg 后可用 heap 命令列出所有 in-use chunk,重点关注 size 大、无变量名、生命周期异常长的小块(如 32/64 字节),可能是对象池未回收或 SSO 失效。
注意:gdb 会暂停进程,务必选低峰期操作;更稳妥方式是先 gcore [pid] 打快照,再离线分析。
根因确认与修复:valgrind + 内核工具组合验证
开发环境或测试机上,用 valgrind 定位 C/C++ 堆泄漏:
- 必须关闭 ASLR:echo 0 | sudo tee /proc/sys/kernel/randomize_va_space,否则调用栈全是 ??:?;
- 编译时禁用优化:gcc -O0,避免行号错位;
- 基础命令:valgrind --leak-check=full --track-origins=yes ./a.out;
- 多线程程序加 --tool=helgrind 查竞态导致的间接泄漏。
内核泄漏则靠:
- slabtop -o(按活跃度排序),查哪个 kmem_cache 对象数暴涨;
- cat /proc/slabinfo | grep -E "(dentry|inode|sock)",结合业务线索(如 curl HTTPS 探测脚本 → dentry 泄漏);
- 确认版本缺陷后,优先打补丁或设环境变量绕过(如 NSS_SDB_USE_CACHE)。








