美国CN2 GIA服务器可用内存持续下降,如何区分缓存占用与进程泄漏?

在“美国服务器使用CN2 GIA线路”的部署场景中,Linux 可用内存持续下降,并不等于服务器一定存在内存泄漏。最先应观察的是 MemAvailable、匿名内存和交换活动,而不是单独盯着 free 或 buff/cache。如果下降主要来自文件页缓存、可回收 slab,且系统在压力出现时能够回收,通常属于正常缓存增长;如果某个进程的 RSS、PSS 或 RssAnon 随时间持续上升,并且回收后仍不下降,才更接近进程泄漏。
最实用的区分方法是同时做两条观察:一条看全局内存是否真的出现压力,另一条看进程占用是否持续集中到特定 PID。缓存占用通常表现为可回收内存增加、MemAvailable 仍有余量;进程泄漏通常表现为匿名内存和某个进程的实际驻留内存同步增长,并可能进一步触发 swap 活动或 OOM。
先确认“可用内存下降”代表什么
Linux 的 free、used、free 和 buff/cache 并不能单独说明故障。文件页缓存本来就是内核用来减少磁盘访问的内存,系统有需要时可以回收。相比“空闲内存”,MemAvailable 更适合作为判断当前还能否在不明显交换的情况下启动新任务的入口。
在 Linux 服务器上先执行以下只读命令:
free -h
重点关注以下字段:
available:内核估算的可用内存,通常比free更有判断价值。buff/cache:缓冲区、文件页缓存和部分可回收内核缓存的合计,不等于永久占用。swap:已使用的交换空间。它说明部分页面曾被换出,但不能单独证明发生了内存泄漏。used:不同版本的procps对该字段的计算方式可能略有差异,不建议只凭这一列下结论。
需要进一步拆分时,可以查看 /proc/meminfo:
grep -E '^(MemTotal|MemFree|MemAvailable|Buffers|Cached|SReclaimable|Shmem|AnonPages|Unevictable|SwapTotal|SwapFree|Dirty|Writeback):' /proc/meminfo
这些字段可以这样理解:
Cached:主要反映文件页缓存,增长后通常具备一定可回收性。SReclaimable:可回收 slab 的重要组成部分,常见于目录项、索引等内核对象。AnonPages:进程堆、栈以及其他匿名映射使用的内存,更应结合进程数据判断。Shmem:共享内存和部分 tmpfs 使用的内存,不能简单当成普通文件缓存。Unevictable:通常不容易被内核回收的内存,持续增长时需要单独调查。SwapFree:与SwapTotal一起反映交换空间剩余情况,但仍需结合实时换入换出判断。
缓存占用与进程泄漏的判断差异
下面的对比适合用于第一轮定位,但任何单项指标都不能替代趋势观察。
| 观察项 | 更符合缓存增长 | 更符合进程泄漏 |
|---|---|---|
MemAvailable | 可能下降,但仍能保持相对稳定或在压力下恢复 | 随时间持续下降,回收后恢复有限 |
Cached、SReclaimable | 增长明显,压力出现时可能下降 | 不一定增长,或者增长无法解释整体下降 |
AnonPages | 通常变化较小 | 持续增长,常与某个进程 RSS/PSS 增长同步 |
| 单个进程 RSS/PSS | 不会长期集中在同一个进程 | 某个 PID 或一组同类进程持续上升 |
| swap | 可能已有少量使用,但换入换出不活跃 | 可用内存不足后出现持续换入换出 |
| OOM 日志 | 通常没有 | 可能出现内核或 cgroup 的 OOM 记录 |
| 业务重启后的表现 | 不重启进程也可能自然回收 | 重启后暂时恢复,随后再次增长 |
这里有两个容易误判的边界。
第一,buff/cache 很高不一定是问题。只要 MemAvailable 仍然足够,swap 没有持续活跃,应用响应也没有因内存回收而明显变慢,缓存往往是在发挥作用。
第二,某个进程 RSS 很高也不一定是泄漏。数据库、编译任务、搜索索引、图片处理等负载可能需要较大的工作集。只有在工作量大致稳定时,进程占用仍持续上升,并且没有合理的缓存上限或回收行为,才应提高对泄漏的怀疑。
按低风险顺序进行排查
1. 建立全局内存基线
不要只执行一次 free -h。连续采样才能判断是瞬时波动还是趋势:
for i in $(seq 1 10); do
date '+%F %T'
awk '/^(MemAvailable|MemFree|Cached|SReclaimable|AnonPages|Shmem|SwapFree|SwapTotal):/ {
printf "%s=%s kB ", $1, $2
} END { print "" }' /proc/meminfo
sleep 60
done
该命令适用于使用 /proc 的 Linux 系统,只读取内核统计信息。观察时至少记录以下关系:
MemAvailable下降,但Cached或SReclaimable上升:优先怀疑缓存增长。MemAvailable和SwapFree同时下降,AnonPages上升:优先检查进程匿名内存。Cached不高,但AnonPages持续增加:进程堆、共享内存或内核外部内存使用更值得关注。- 各项都波动,但长期均值没有下降:可能是业务高峰或正常工作集变化,不宜直接判定泄漏。
如果系统支持内存压力指标,还可以查看:
cat /proc/pressure/memory
some 或 full 的压力时间增加,说明任务确实在等待内存资源,而不仅是显示上“空闲内存变少”。该指标用于判断系统是否受压,不能单独指出是哪一个进程造成问题。
2. 判断 swap 是“历史使用”还是“正在抖动”
先查看交换设备和当前状态:
swapon --show
free -h
vmstat 1 5
vmstat 输出中的 si 和 so 分别表示换入、换出活动。应重点看间隔采样产生的后几行:
- swap 已使用,但
si、so长时间接近零:可能只是较早被换出的冷页面,不能据此判断泄漏。 si、so持续有活动,同时MemAvailable低、业务延迟升高:系统已经出现内存压力。- swap 频繁抖动,但进程 RSS 并未持续增长:可能是工作集过大、并发突然升高或内存配置不足,需要结合业务负载。
- 没有配置 swap:不会产生换入换出,但内存压力可能更快表现为回收、分配失败或 OOM。
不要把“关闭 swap”当成排查手段。它可能减少可用缓冲空间,使内存压力更快转化为进程被终止。
3. 找出实际占用内存的进程
使用 ps 查看当前快照:
ps -eo pid,ppid,user,%mem,rss,vsz,stat,etime,comm,args --sort=-rss | head -n 20
这里的 rss 和 vsz 通常以 KiB 显示:
RSS是当前驻留在物理内存中的进程页面,但会包含部分共享页面。VSZ是进程虚拟地址空间大小,不能直接等同于实际物理内存。%MEM是相对于系统物理内存的比例,只适合做排序和初筛。ETIME可以帮助判断进程是长期运行后增长,还是刚启动就占用较高。
如果某个 PID 排名靠前,只能说明它当前占用较多,还不能证明泄漏。需要记录同一 PID 的数据,观察 RSS 是否持续上升:
PID=1234
grep -E '^(Name|Pid|VmSize|VmRSS|RssAnon|RssFile|RssShmem|VmSwap):' /proc/"$PID"/status
将 1234 替换为实际 PID。若进程已经退出,/proc/PID 会消失,这是正常现象。
判断时可参考:
RssAnon持续上升:更接近堆、栈或匿名映射增长。RssFile上升:可能是加载的文件、动态库或文件映射,不一定是泄漏。RssShmem上升:应检查共享内存、tmpfs 或进程间通信机制。VmSwap上升:说明该进程部分页面被换出,但不代表这些页面就是泄漏。VmSize很大而VmRSS稳定:可能只是预留了虚拟地址空间,不能按虚拟内存大小处理。
4. 用 PSS 排除共享内存重复计算
多个进程共享同一组页面时,简单相加 RSS 可能高估实际占用。Linux 提供 smaps_rollup 时,可以查看更适合比较的 PSS:
PID=1234
if [ -r /proc/"$PID"/smaps_rollup ]; then
awk '/^(Rss|Pss|Private_Clean|Private_Dirty|Shared_Clean|Shared_Dirty|Swap):/ {
print
}' /proc/"$PID"/smaps_rollup
else
echo "当前内核未提供 /proc/$PID/smaps_rollup"
fi
PSS 会按比例分摊共享页面,比较多个进程时通常比 RSS 更有参考价值。重点观察的是一段时间内的增长速度,而不是某一时刻的绝对值:
- RSS、PSS、
Private_Dirty同步持续增长,更支持进程私有数据未释放。 - RSS 增长但 PSS 基本稳定,可能是共享页面变化或统计重复。
Private_Clean或RssFile增长,可能来自文件映射或代码页,不应直接定义为堆泄漏。- 只有进程重启后数值暂时下降,不能证明已经找到根因;重启只是清空了该进程的地址空间。
如果系统安装了 sysstat,可以使用 pidstat 连续观察进程:
pidstat -r -p 1234 60 10
该命令适用于已安装 sysstat 工具的 Linux 系统。若提示命令不存在,不要据此安装不确定的软件包,直接使用 /proc/PID/status 或 smaps_rollup 做定时采样即可。
5. 检查是否已经发生 OOM
当内存下降伴随进程突然退出、服务自动重启或请求中断时,应查看内核日志:
sudo journalctl -k --since "2 hours ago" --no-pager | \
grep -Ei 'out of memory|oom|killed process|memory cgroup'
如果系统没有使用 systemd 日志,也可以在具备权限且允许读取内核日志的前提下执行:
sudo dmesg -T | grep -Ei 'out of memory|oom|killed process|memory cgroup'
日志中的关键信息包括:
Out of memory或Killed process:说明内核曾执行 OOM 处理,但被杀的进程不一定是根因。Memory cgroup out of memory:更可能是某个服务所在的 cgroup 达到限制,即使整台服务器仍有可用内存。- 没有 OOM 日志:不能证明没有内存问题,可能只是发生了 swap 抖动、应用自身退出,或者日志保留时间不足。
如果进程由 systemd 管理,可以只读检查服务的内存限制:
systemctl show your-service.service \
-p MemoryCurrent -p MemoryHigh -p MemoryMax -p OOMPolicy
将 your-service.service 替换为实际服务名。若 MemoryMax 设置得较小,服务达到该上限时可能被 cgroup 终止;此时应先区分“服务触及人为限制”和“整台服务器物理内存不足”。
还可以查看进程属于哪个 cgroup:
cat /proc/1234/cgroup
cgroup 路径需要结合当前系统的 cgroup 版本和挂载位置进一步解释。不同发行版、systemd 版本和容器环境的路径可能不同,不应直接套用其他环境中的路径。
如何从结果反推根因
情况一:缓存上涨,但系统没有明显压力
典型表现是 Cached 或 SReclaimable 增长,AnonPages 和主要进程的 PSS 基本稳定;MemAvailable 虽下降但仍能维持,vmstat 中 swap 活动很少,内核日志也没有 OOM。
这种情况通常不需要清理缓存。Linux 使用空闲内存做文件缓存,可以减少后续读取开销。手工清理缓存只会带来短暂的数字变化,并可能增加磁盘读取和业务延迟,不能修复真正的进程泄漏。
应继续观察两个条件:
- 业务高峰或文件访问结束后,缓存是否保持稳定或能够被回收。
- 新任务启动时,
MemAvailable是否明显下降并伴随持续 swap 或内存压力。
如果这些条件正常,所谓“可用内存减少”更可能是缓存利用率提高,而不是异常。
情况二:某个进程的匿名内存持续增长
典型表现是 AnonPages 增长,某个 PID 的 RssAnon、PSS 或 Private_Dirty 在相同业务量下持续上升,其他进程变化不大;进程重启后暂时恢复,运行一段时间又重复出现。
这时应保留以下证据,而不是立即反复重启:
- 进程 PID、启动时间和内存采样记录。
- 对应时间段的请求量、任务量和并发变化。
- 应用日志中的连接、队列、缓存、批处理或异常重试信息。
- 最近一次发布、配置变更和依赖升级记录。
- OOM 日志以及服务管理器的重启记录。
如果必须通过重启缓解,先确认服务支持优雅退出、关键状态已落盘,并在维护窗口执行。重启会影响该服务及其正在处理的请求,可能丢失未提交的内存状态;它的回滚方式通常是恢复此前版本或配置后再按服务管理器启动。重启只能作为临时止血,不能替代对堆、连接、队列和缓存生命周期的定位。
情况三:swap 使用量上升,但没有明确的泄漏进程
如果 swap 已使用,但 si、so 很低,可能只是冷页面被保留在交换空间中。相反,如果换入换出持续发生,且系统响应变慢,应把问题视为内存压力,而不是单纯的“swap 数字变大”。
此时需要重新核对:
- 是否有短时间并发或批处理任务造成工作集扩大。
- 是否有多个进程同时增长,导致单个进程看起来并不突出。
- 是否存在 cgroup 的
MemoryHigh或MemoryMax限制。 MemAvailable是否与业务延迟、磁盘等待或内存压力同步变化。
不要仅凭 swap 使用率设定固定阈值。不同工作负载对交换的容忍度不同,持续换入换出和业务实际受阻比“用了多少 swap”更有判断价值。
适用边界与常见误区
上述命令适用于能够读取 /proc、使用 Linux 内存统计接口的服务器。命令中的 PID、服务名和采样时间必须替换为现场值;不同内核版本可能缺少 smaps_rollup、压力指标或部分 meminfo 字段,缺失时应采用可用字段,不要伪造结果。
还应注意以下边界:
- RSS、PSS 和系统
Cached统计口径不同,不能把各列简单相加。 free很低不等于内存耗尽,必须结合MemAvailable和压力指标。- 进程一次性占用很高不等于泄漏,持续增长趋势才是关键证据。
- OOM 被杀的进程不一定是最初消耗内存的进程,应结合采样和日志回溯。
- cgroup OOM 与整机 OOM 是两类事件,前者可能在宿主机仍有剩余内存时发生。
- 清理缓存、盲目关闭 swap、随意提高或降低内存限制,都可能改变故障表现,不能代替定位。
最终可以按以下标准做出判断:如果主要增长的是可回收缓存,MemAvailable 尚可、swap 不活跃、没有 OOM,优先按正常缓存处理;如果 AnonPages 与特定进程的 RSS/PSS 同步持续增长,且在相同工作量下无法回落,应按进程泄漏或未受控工作集调查;如果已经出现持续 si/so、内存压力或 OOM 日志,则先记录现场、确认整机还是 cgroup 限制,再进行服务级处置。