
Linux 内存持续增长,不一定是内存泄漏,更可能是内核缓存(page cache、slab、buffers)或用户进程真实占用增加。定位需分层排查:先看整体趋势,再聚焦进程和内核内存分布,最后结合时间线与行为特征判断根因。
一、确认是否真存在“异常增长”
Linux 的“可用内存”低不等于有问题。内核会主动利用空闲内存做缓存(如文件缓存、目录项缓存),这是正常且有益的行为。关键看:
- free -h 中 available 值是否持续下降并逼近 0(而非仅 used 高);
- 是否伴随 OOM Killer 触发(dmesg | grep -i "killed process");
- swap 使用量是否持续上升(swapon --show);
- 系统响应变慢、大量 page fault 或 major fault(sar -B)。
若仅 used 高但 available 充足、无 swap、无 OOM,大概率是缓存行为,无需干预。
二、区分用户态 vs 内核态内存占用
用以下命令快速划分责任域:
- 用户进程总占用:`ps aux --sort=-%mem | head -10` 查内存 top 进程;注意 RSS 和 VSZ 区别,RSS 更反映实际物理内存占用;
- 内核内存(非进程):`cat /proc/meminfo | grep -E "^(Cached|Buffers|SReclaimable|Slab|PageTables|KernelStack)"`;其中 Slab(尤其是 dentry/inode)和 PageTables 增长快常指向内核对象泄漏;
- 内存映射总量:`cat /proc/meminfo | grep "^Mapped"`,过高可能有大量 mmap 未释放或共享库加载异常。
三、定位具体泄漏源(进程级)
对可疑进程(如 RSS 持续上涨的 Java/Python/Go 服务)进一步分析:
-
查看其内存映射详情:`pmap -x
`,关注 anon-rss(匿名页,通常是堆/栈)、mapped 文件大小变化; -
检查堆内存(Java):`jstat -gc
` 看 old gen 是否持续增长且 GC 不回收;配合 `jmap -histo:live ` 查对象分布; -
C/C++ 类程序:用 `valgrind --tool=memcheck --leak-check=full`(需预编译调试符号)或 `gdb -p
` + `info proc mappings` + `dump memory` 辅助分析; - Go 程序:`go tool pprof http://localhost:6060/debug/pprof/heap`,重点关注 inuse_space 趋势。
四、排查内核侧常见泄漏点
当 Slab、SReclaimable、PageTables 显著增长时:
- 查 slab 分配热点:`slabtop -o`(按活跃对象排序),重点关注 dentry、inode_cache、ext4_inode_cache、kmalloc-*;
- 确认 dentry/inode 泄漏:`find /proc/*/fd -ls 2>/dev/null | grep deleted | wc -l`(大量已删除仍被打开的文件句柄);或 `lsof +L1` 查链接数为 0 的文件;
- 检查 ext4/jbd2 日志相关缓存:`cat /proc/fs/ext4/*/stats | grep -E "(dirty|io_)"`,写密集场景 journal 缓存堆积可能延迟回收;
- 确认是否开启透明大页(THP)副作用:`cat /sys/kernel/mm/transparent_hugepage/enabled`,某些 workload 下 THP 合并失败会导致内存碎片和额外开销,可临时禁用测试:`echo never > /sys/kernel/mm/transparent_hugepage/enabled`。
定位需结合监控(如 node_exporter + Grafana 记录 meminfo、slab、process_resident_memory)、复现节奏(是否随请求量线性增长?是否某次部署后开始?)和日志交叉验证。多数情况下,问题落在应用层内存管理不当或内核对象生命周期异常,而非 Linux 本身缺陷。








