Linux 服务器线上运行一段时间后,监控面板里的内存使用率曲线频繁冲高,往往让运维人员第一时间想到加内存或重启。但多数情况下,这种“过高”只是内核利用空闲内存做缓存的正常机制,或者是某个长期运行的服务在缓慢泄漏。只有搞清楚内存都去哪儿了,才能对症下药。

一、先看清内存统计的真实含义
很多人习惯用 free -h 命令看内存,但默认输出里的 used 包含了页缓存(page cache),而这部分在内核需要时会立刻释放给应用。因此 used 高并不代表内存紧张。更合理的做法是直接读取 /proc/meminfo,关注 MemAvailable 以及 SReclaimable、SUnreclaim 等字段。
页缓存用于加速文件读写,属于可回收内存;slab 中的 SReclaimable 是目录项、索引节点等可回收内核对象,SUnreclaim 则是不可回收部分,若它持续上涨通常暗示内核级泄漏或驱动问题。用户态进程的匿名内存对应 AnonPages,这部分才是真正挤占可用内存的来源。
# 查看关键内存指标 cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable|Buffers|Cached|SReclaimable|SUnreclaim|AnonPages" # 输出示例 # MemTotal: 16384000 kB # MemFree: 512000 kB # MemAvailable: 8200000 kB # Cached: 6000000 kB # SReclaimable: 800000 kB # SUnreclaim: 350000 kB # AnonPages: 7000000 kB
二、用工具定位是哪个进程在吃内存
传统 top 命令的 RES 列显示常驻内存,但无法区分共享内存的重复计算。更推荐 smem,它能按比例分摊共享内存,给出更准确的 PSS(按比例占用物理内存大小)。安装后执行 smem -s pss -r | head 即可看到排在前面的进程。
如果发现某个 Java 或 Python 服务 PSS 每隔几天就翻一倍,而业务量并没有增长,那大概率是堆外内存泄漏或缓存未设上限。此时可进一步用 pmap -x 进程号 查看内存映射,寻找异常大的匿名段。对于支持 Native Memory Tracking 的 JVM,还可开启对应参数做细分。
# 安装并查看进程实际物理内存占用排行 sudo apt install smem smem -s pss -r | head -n 10 # 查看某进程的内存映射分布 pmap -x 1234 | tail -n 20
三、常见原因与对应处理方案
第一类是应用层缓存无限增长。比如自研服务用 HashMap 做本地缓存却忘了淘汰策略,解决方法是引入 LRU 或 TTL,或者改用 Caffeine、Redis 等带边界的组件。第二类是内存泄漏,需要借助 Valgrind、AddressSanitizer 或语言自带的剖析器定位未释放对象。
第三类是系统层面 swap 被禁用或过小,导致匿名页无法换出,一旦突发分配就直接 OOM。可适当开启 swap 并调整 swappiness(如设为 10)让系统更积极回收页缓存而非杀进程。第四类是内核模块或文件系统缓存不释放,可周期性执行 echo 2 > /proc/sys/vm/drop_caches 仅回收 slab 可回收部分,但生产环境需评估对 IO 的影响。
| 现象 | 可能原因 | 处理手段 |
|---|---|---|
| MemAvailable 低,AnonPages 高 | 用户态进程占用多 | 限制服务内存、排查泄漏 |
| SUnreclaim 持续增长 | 内核/驱动泄漏 | 更新驱动、重启节点 |
| Cached 高但可用少 | 脏页或 pinned 缓存 | 调整 drop_caches、写回策略 |
四、建立长效防控机制
单靠人工排查不能杜绝问题。建议在监控系统中除采集 used 外,重点绘制 MemAvailable、SUnreclaim、主要进程 PSS 的时序图,并设置基于趋势的预警而非绝对值。例如当 SUnreclaim 周环比增长超百分之三十即告警。
同时,对关键服务配置 cgroup 内存上限,避免单点拖垮整机。使用 systemd 的服务可在单元文件里写 MemoryMax=2G,超限额后被杀死而非挤占别人。配合定期的堆栈与内存剖析任务,能把大部分“频繁过高”消灭在萌芽阶段。
# /etc/systemd/system/myapp.service 片段 [Service] ExecStart=/usr/bin/python3 /opt/myapp/main.py MemoryMax=2G MemoryHigh=1.5G
五、应急操作清单
当线上已经告警且需要快速缓解,可依次执行:确认是否误报(看 MemAvailable)、找出 top PSS 进程、对非核心服务重启、临时放宽 swap 策略、必要时手动回收 slab。切忌一上来就 reboot,那样会丢失现场导致无法根因分析。
下面给出一个简易排查脚本,适合放进定时任务做粗筛,发现异常再人工介入。它只做数据抓取,不自动处理,降低误操作风险。
#!/bin/bash
# 简易内存异常记录脚本
ts=$(date '+%F %T')
avail=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
sunreclaim=$(grep SUnreclaim /proc/meminfo | awk '{print $2}')
if [ "$avail" -lt 1048576 ]; then
echo "$ts WARN avail=${avail}KB sunreclaim=${sunreclaim}KB" >> /var/log/mem_check.log
fi
通过上述分层方法与工具组合,Linux 内存使用率频繁过高的问题通常能在不增加硬件成本的前提下被理清并稳定控制。核心始终是区分缓存与真实占用,以及把排查动作常态化。