当top/htop没有明显指向罪魁祸首时。以下是一套系统性的排查方法,帮助你找到“隐藏”的内存消耗者:核心思路: 内存消耗不一定直接体现在某个用户态进程的RES或%MEM上。它可能来源于:- 内核本身: 内核数据结构(如slab缓存、页表、网络缓冲区等)泄漏或过度增长。
- 内核模块/驱动: 某个加载的内核模块(尤其是硬件驱动)存在内存泄漏。
- 短命进程: 频繁启动又退出的进程,在top的刷新间隙难以捕捉。
- 缓存/缓冲区的变化: 虽然free显示available低,但Linux会积极利用内存做缓存和缓冲,这部分在free命令里属于buff/cache,当应用程序需要时会被回收。但如果available持续降低且free也很低,说明缓存/缓冲不是主因,而是有真实占用。
- 内存泄漏的用户态进程: 进程的内存泄漏可能分散在虚拟地址空间的各个角落,top显示的RES可能增长缓慢或不明显,但VIRT可能很高,或者其占用的共享库、tmpfs等有异常。
1.free -h / free -m (多次运行观察趋势):- 重点关注 available 列。这是系统认为真正可用给应用程序的内存(≈ free + buff/cache 中可回收的部分)。如果它持续下降,说明有真实的内存被占用且无法被简单回收。
- 观察 buff/cache:如果它很大但 available 也还充足,通常是正常现象(系统利用空闲内存加速IO)。如果 available 很低而 buff/cache 很大,尝试手动释放:sync; echo 3 > /proc/sys/vm/drop_caches (需要root)。操作后观察 available 是否显著回升。如果回升有限,说明主要问题不是缓存/缓冲区,而是其他占用。
- 观察 used 和 free:free 很低是警示信号,但结合 available 和 buff/cache 看更有意义。
1.slabtop (需要root):- 这是最关键的工具之一!它显示内核slab分配器的缓存使用情况。slab用于管理内核对象(如inodecache, dentry, skbuffheadcache, vmarea_struct等)。
- 运行 sudo slabtop -s c (按缓存大小降序排序)。
- 重点观察: 哪个Cache名称占用的OBJS数量和SIZE异常巨大?特别关注那些持续增长的项。例如,dentry或inode_cache异常高可能表示文件系统元数据缓存泄露;网络相关的skbuff高可能表示网络问题;驱动相关的模块名出现且增长也要注意。
2.cat /proc/meminfo (详细内存分项统计):- 这个文件提供了极其详细的内存使用分类。运行 cat /proc/meminfo 并重点关注以下项:
- Slab: 总Slab使用量 (应与slabtop的总和对应)。
- SReclaimable: 可回收的Slab (如dentry, inode cache)。
- SUnreclaim: 不可回收的Slab。这个值持续增长是内核或驱动内存泄漏的强烈信号!
- PageTables: 页表占用。如果系统运行了大量进程或使用了大量内存映射,可能会很高。
- KernelStack: 内核栈占用。每个线程消耗一点。
- VmallocUsed: 通过vmalloc分配的内核空间。某些驱动或模块会使用。
- HugePages_: 大页相关统计(如果启用了)。
- SwapCached: 换出过但又换回的内存,仍在swap缓存中。
- Active(file)/Inactive(file): 活跃/不活跃的文件缓存。
- Active(anon)/Inactive(anon): 活跃/不活跃的匿名内存。
- Unevictable: 无法换出的内存(如mlock的或某些shm)。
- Committed_AS: 当前所有进程已申请的虚拟内存总量(可能超过物理内存+交换空间)。
- 方法: 定期(如每隔几分钟)运行 cat /proc/meminfo | egrep 'Slab|SReclaimable|SUnreclaim|PageTables|KernelStack|VmallocUsed|Committed_AS' 并记录值,观察哪些项在持续增长。SUnreclaim的增长是最危险的。
- 按内存排序: ps -eo pid,ppid,user,%mem,%cpu,rss,vsz,cmd --sort=-%mem | head -n 20 或 ps -eo pid,ppid,user,%mem,%cpu,rss,vsz,cmd --sort=-rss | head -n 20。查看RSS(常驻内存)或%MEM最高的进程。虽然top也做这个,但ps的输出格式可能更易分析历史记录或脚本处理。
- 按虚拟内存排序: ps -eo pid,ppid,user,%mem,%cpu,rss,vsz,cmd --sort=-vsz | head -n 20。高VSZ(虚拟内存)可能指示内存泄漏但尚未完全提交物理页的进程,或者使用了大量共享库/映射文件的进程。
- smem 提供了更智能的内存报告,特别是它能区分独占内存(USS) 和比例集大小(PSS)。PSS更公平地计算了共享库的内存占用(将共享内存按使用进程数均摊)。
- 安装:sudo apt install smem (Ubuntu/Debian系麒麟) 或 sudo yum install smem (CentOS/RHEL系麒麟)。
- sudo smem -s pss -r: 按PSS降序排序,最公平。
- sudo smem -s uss -r: 按USS降序排序,看独占内存。
- sudo smem -t -k: 带总计的表格输出,单位KB。
优势: 能更准确地找出真正消耗物理内存的进程,尤其是那些共享库很多的进程。- atop 是一个强大的交互式性能监控工具,功能远超top。它能记录历史数据,并提供更详细的进程级内存统计(包括进程占用的PSS、RSZ、VSZ,以及进程使用的页表大小等)。
- 安装:sudo apt install atop 或 sudo yum install atop。
- 特别关注 MEM 列下的 RSS (类似top的RES), PSS (更公平), VGROW (虚拟内存增长量), RGROW (常驻内存增长量)。VGROW/RGROW高的进程即使当前绝对值不高,也值得怀疑。
- 按 c 切回进程视图,按 d 可以显示磁盘,按 n 显示网络。
- atop 默认会记录历史(通常每天一个日志文件在/var/log/atop),可以回放分析:atop -r /var/log/atop/atop_YYYYMMDD (按 t 前进)。
- auditd 系统: 如果系统启用了auditd,可以尝试配置规则记录所有进程执行(生产环境慎用,日志量巨大):
sudo auditctl -aexit,always -F arch=b64 -S execve
sudo auditctl -aexit,always -F arch=b32 -S execve
然后分析 /var/log/audit/audit.log,查找频繁执行的命令。- sysdig/csysdig 工具: 非常强大的动态追踪工具,可以实时捕获所有系统调用和事件。
- 实时捕获进程执行:sudo sysdig "proc.name != ''" 或 sudo csysdig (交互式界面,按 F2 选择视图)。
- execsnoop (来自bcc/bpftrace工具集): 专门设计来追踪短命进程。需要安装bpfcc-tools或bpftrace。
sudo execsnoop-bpfcc # 或 sudo execsnoop (取决于包名)
- 检查内核环形缓冲区日志:dmesg -T | tail -n 100 或 dmesg -T --level=err,warn。特别留意是否有 Out of memory: Kill process ... (OOM killer) 信息、slab分配失败、驱动加载/卸载错误、硬件错误等。
- 使用journalctl查看系统日志:sudo journalctl -b 0 --since "1 hour ago" -p 3 (查看本次启动以来,优先级为error及以上的日志,时间范围最近1小时)。调整时间范围和优先级过滤。
- 结合/proc/meminfo中的SUnreclaim增长和slabtop中发现的异常缓存项,怀疑特定驱动模块。可以尝试谨慎地卸载最近加载的、非关键的可疑模块(sudo modprobe -r <module_name>),观察内存增长是否停止。卸载驱动有风险,可能导致硬件失效或系统不稳定! 最好在测试环境或确定模块可卸载时操作。
sudoperfrecord-ekmem:kmalloc-ekmem:kfree-ekmem:kmem_cache_alloc-ekmem:kmem_cache_free-ag
# 运行一段时间后 Ctrl+C
sudoperfreport
分析报告,看哪些内核函数分配了大量内存且没有释放。- 如果麒麟系统内核编译时启用了CONFIGDEBUGKMEMLEAK,这是一个专门检测内核内存泄漏的机制。需要挂载debugfs,然后:
echo scan > /sys/kernel/debug/kmemleak # 触发扫描
cat /sys/kernel/debug/kmemleak # 查看可能的泄漏点报告
- 确认问题: 持续运行 free -h 或 watch -n 5 "free -h; echo; date",明确available内存确实在持续下降,且手动drop_caches后回收有限。
- 内核嫌疑: 立即使用 sudo slabtop -s c 和 watch -n 5 "cat /proc/meminfo | egrep 'Slab|SReclaimable|SUnreclaim|Committed_AS'"。如果SUnreclaim持续增长,高度怀疑内核或驱动泄漏。 记录slabtop中增长最快的缓存项。
- 进程再排查: 安装并使用 smem -s pss -r 和 sudo atop (重点看PSS, VGROW, RGROW)。这些工具比top更能揭示真实内存消耗。
- 日志检查: 仔细查看 dmesg -T 和 sudo journalctl -b 0 --since "yesterday" -p 3 中的错误、警告和OOM信息。
- 短命进程: 如果怀疑,使用 execsnoop 或 sysdig 进行捕捉。
- 驱动/模块: 根据slabtop和日志线索,检查lsmod,在充分评估风险后尝试卸载可疑的非关键驱动模块。
- 高级诊断: 如果上述步骤仍无头绪,考虑使用perf追踪内存分配,或检查内核是否支持kmemleak。
- 备份: 在进行可能影响系统稳定性的操作(如卸载模块)前,确保有备份或能在物理接触机器。
- 环境: 如果是生产环境,操作需格外谨慎,尽量在业务低峰期进行,并做好回滚准备。
- 持续监控: 使用atop记录历史或编写脚本定期捕获/proc/meminfo和slabtop输出,便于分析增长趋势。