上一篇 下一篇 分享链接 返回 返回顶部

香港服务器可用内存为何越来越少:如何区分缓存增长与进程泄漏

发布人:Minchunlin 发布时间:20小时前 阅读量:19
香港服务器可用内存为何越来越少:如何区分缓存增长与进程泄漏

先判断“可用内存变少”是否等于内存泄漏

香港服务器运行 Linux 时,系统会主动把暂时空闲的内存用于文件缓存、目录索引和内核可回收对象。因此,free 变小、used 变大,并不能直接证明某个进程泄漏内存。

真正需要先确认的是:MemAvailable 是否持续下降,交换分区是否发生持续读写,进程的常驻内存是否随时间单调增长,以及是否出现 OOM(Out of Memory)记录。简单地说:

  • free 少、buff/cache 多、MemAvailable 仍有余量,通常是缓存利用,不一定是故障。
  • MemAvailable 持续下降,某个进程的 RSS、PSS 或匿名内存同步增长,更接近进程占用异常。
  • Swap 已使用不等于当前正在发生内存压力,还要结合 vmstat 中的换入换出活动判断。
  • OOM 可能发生在整台服务器,也可能只发生在某个容器或 systemd cgroup 内,不能只看主机总内存。

先在业务负载相对稳定时执行以下只读命令,建立一组基线:

date
uptime
free -h
grep -E 'MemTotal|MemFree|MemAvailable|Buffers|Cached|SReclaimable|SUnreclaim|Shmem|AnonPages|SwapTotal|SwapFree' /proc/meminfo
vmstat 1 5
ps -eo pid,ppid,comm,rss,vsz,%mem --sort=-rss | head -n 15

vmstat 1 5 的第一行通常是启动以来的平均值,判断当前压力时应重点观察后续按秒输出的行。

这些指标分别说明什么

MemFree 不是最重要的可用内存指标

Linux 会尽量避免内存闲置,所以 MemFree 很低并不必然异常。判断系统还能否较平稳地承受新负载,应优先看:

  • MemAvailable:内核根据当前可回收页、空闲页和内存水位估算出的可用内存。
  • Swap 活动:是否持续发生页面换入和换出。
  • 内存压力和业务延迟:是否因为回收、换页而出现等待。
  • OOM 事件:是否已经无法满足分配请求。

MemAvailable 是估算值,不是保证可以完整分配给单个进程的连续内存,也不代表所有内存都能立即回收。内核版本、文件缓存、内存碎片和大页使用情况都会影响它。

常见指标的判断方式

指标主要含义能支持的判断不能单独证明的事情
MemFree当前完全空闲的内存空闲页数量不能代表服务器实际还能使用的全部内存
MemAvailable内核估算的可用内存判断整体余量是否持续下降不能保证任意一次大内存分配一定成功
Cached文件页缓存等可回收页面判断文件访问是否产生较多缓存不能说明缓存一定能在瞬间全部释放
SReclaimable一部分可回收内核 slab判断目录、inode 等内核对象是否占用较多空间不能把所有 slab 都视为立即可回收
SwapFree 或 SwapUsed交换空间的占用情况判断是否有页面曾被移出物理内存不能单独证明当前正在发生严重内存压力
vmstat 的 si、so每个采样周期的换入、换出活动判断当前是否持续换页一次短暂的非零值不能证明进程泄漏
进程 RSS进程当前驻留在物理内存中的页面找出占用较大的进程共享页面可能被多个进程重复计入
进程 PSS按比例分摊共享页面后的占用更合理地比较多个进程的实际内存归属仍不能单独判断“有意缓存”还是“泄漏”
RssAnon进程的匿名常驻内存观察堆、匿名映射等增长增长也可能来自正常业务缓存或并发提升
OOM 日志内核或 cgroup 曾触发内存不足处理确认发生过 OOM 处置没有日志不代表一定没有压力,日志可能已轮转或权限不足

free -h 中的 buff/cache 通常包含文件缓存、buffer 和一部分可回收对象。它较高时,若 MemAvailable 稳定、si/so 基本没有持续活动,通常不应仅凭这个数字清理缓存或重启服务。

如何确认是正常缓存增长

典型特征

正常的内核缓存通常具有以下表现:

  1. 服务器读取大量文件后,Cached 或相关可回收项目增加。
  2. 业务停止或访问模式改变后,缓存增长趋于平稳。
  3. 发生真实内存需求时,内核可以回收其中一部分页面。
  4. MemAvailable 没有随着缓存增加而持续快速下降。
  5. vmstat 中没有持续的 si、so,业务延迟也没有明显因换页升高。

需要注意,应用自身的缓存不一定出现在 Cached 中。例如,应用将对象、结果集或会话数据保存在堆内存中时,它们通常属于匿名内存,会体现在进程的 RSS 或 PSS 中。此时不能因为它被称为“缓存”就认为它可以被内核自动回收。

可以用下面的命令观察系统级内存类别:

free -h

grep -E 'MemAvailable|Cached|Buffers|SReclaimable|SUnreclaim|AnonPages|Shmem|SwapFree' /proc/meminfo

swapon --show

如果 Cached 增长,但 MemAvailable 仍有相对稳定的余量,且换页活动很低,优先把它视为系统在利用内存提高文件访问效率。这个判断适用于负载和内存总量没有突然变化的时间段;如果同时出现业务延迟、MemAvailable 快速下降或 OOM,就不能只用“缓存正常”解释。

不要把清理缓存当成泄漏检测

生产环境中不应把手动清理 page cache 作为常规排查手段。清理缓存可能造成后续文件读取重新访问存储,短时间内增加 I/O 和延迟,而且即使清理后内存数字变好,也不能证明之前存在进程泄漏。

如果确实需要验证缓存回收能力,应优先在测试环境或可接受影响的维护窗口中进行,并记录操作前后的:

  • MemAvailable
  • Cached、SReclaimable
  • vmstat 的 si/so
  • 业务延迟和 I/O 等待
  • 主要进程的 RSS、PSS

在生产香港服务器上,更安全的做法是观察自然负载变化,或使用与生产相近的受控压力测试,而不是直接制造内存压力。

如何确认是进程占用增长或疑似泄漏

先看趋势,不要只看一次排名

ps 排名只能告诉你当前谁占用较多,不能告诉你谁在泄漏。应在相同或相近业务负载下连续采样,关注同一个 PID 的变化。

ps -eo pid,ppid,comm,rss,vsz,%mem --sort=-rss | head -n 20

其中:

  • RSS 通常以 KiB 显示,是当前驻留内存。
  • VSZ 是虚拟地址空间大小,包含尚未实际占用物理内存的区域,不适合直接当作内存消耗。
  • %MEM 是相对整机物理内存的比例,容器或 cgroup 场景下不能直接代表该服务相对自身限制的比例。

对目标进程进一步检查:

PID=1234

if [ -r "/proc/$PID/smaps_rollup" ]; then
    cat "/proc/$PID/smaps_rollup"
else
    grep -E 'VmRSS|VmSwap|RssAnon|RssFile|RssShmem' "/proc/$PID/status"
fi

smaps_rollup 并非所有内核环境都提供,因此命令先检查文件是否存在。重点观察:

  • Pss:按比例计算共享页后的总占用。
  • Rss:进程当前驻留内存。
  • Private_Clean、Private_Dirty:更接近进程独占的页面。
  • Swap:该进程有多少页面已经进入交换空间。
  • /proc/PID/status 中的 RssAnon:匿名内存占用。
  • RssFile:文件映射或文件相关页面。

具备哪些条件才接近“泄漏”

单次看到某进程内存较高,不足以判定泄漏。更有价值的证据是:

  • 在业务并发和请求类型大致相同的情况下,进程 PSS 或 RssAnon 持续上升。
  • 一个批次、请求或任务结束后,内存不再回落,且重复执行相同工作后继续抬高。
  • 增长主要来自匿名私有内存,而不是可以回收的文件页面。
  • 进程增长与整机 MemAvailable 下降、Swap 活动或 cgroup 限制触发同步出现。
  • 对象数量、连接数、线程数、队列长度或应用内部缓存统计也出现同方向增长。

可以用简单的循环记录趋势。这个命令只读取 /proc,不会终止进程:

PID=1234

while [ -r "/proc/$PID/status" ]; do
    printf '%s ' "$(date '+%F %T')"
    awk '/^(VmRSS|VmSwap|RssAnon|RssFile|RssShmem):/ {printf "%s=%s%s ", $1, $2, $3}' "/proc/$PID/status"
    printf '\n'
    sleep 60
done

采样时应同时记录业务量,否则容易把正常扩容误判为泄漏。例如,请求并发增加、批处理规模变大、连接池扩大、应用缓存预热,都可能使内存上升。只有在负载回到相近水平后仍不回收,泄漏嫌疑才更强。

如果系统安装了 sysstat,也可以使用 pidstat 查看进程内存变化:

pidstat -r -p 1234 60

该命令是否可用取决于发行版是否安装了 sysstat。它提供的是采样数据,不会解释增长原因,仍需结合进程自身的堆、缓存和连接指标。

RSS 增长也不一定就是代码泄漏

以下情况可能造成内存长期保持较高,但未必是传统意义上的泄漏:

  • 内存分配器为了后续复用,暂时不把已释放的堆空间归还给内核。
  • 垃圾回收器已经释放对象,但运行时保留了堆容量。
  • 线程、连接池或工作队列按峰值扩容后没有及时缩回。
  • 共享库、共享内存和文件映射的统计方式造成误判。
  • 应用缓存有明确上限,但业务规模正在增长。
  • 大页、内存映射文件或临时文件映射改变了内存分布。

因此,重启服务后内存下降只能说明该进程原先持有的内存被释放了,不能单独证明存在泄漏。若重启后在相同负载下再次按近似速度增长,应继续收集应用层内存统计或堆分析数据。

交换空间和 OOM 应该怎样解读

Swap 已使用不等于正在发生故障

Linux 可能把一段时间不活跃的匿名页面放入 Swap,为文件缓存或当前活跃页面保留物理内存。因此,SwapUsed 有数值时,服务器仍可能运行正常。

判断当前是否存在交换压力,应结合:

vmstat 1 10

重点观察连续采样中的:

  • si:从交换空间读入内存的活动。
  • so:从内存写入交换空间的活动。
  • wa:I/O 等待变化。
  • r、b:运行队列和阻塞任务变化。

如果 Swap 已经使用,但 si、so 长时间接近零,通常只能说明过去有页面被换出。若 si、so 持续升高,同时 MemAvailable 较低、业务响应变慢,就说明工作集已经超出当前可快速驻留的内存范围。此时应查清是进程增长、并发突增、cgroup 限制,还是内核内存占用增加。

检查系统级和服务级 OOM

Linux 内核日志可以帮助确认是否发生过 OOM:

journalctl -k -b | grep -Ei 'out of memory|oom-killer|killed process'

某些环境没有 journalctl,或者普通用户无法读取内核日志,可以在具备权限的前提下尝试:

dmesg | grep -Ei 'out of memory|oom-killer|killed process'

日志中如果出现被杀进程、触发原因和内存统计,说明内核已经执行了 OOM 处理。日志缺失可能是日志轮转、容器隔离、权限限制或记录位置不同造成的,不能把“没有搜到”当作“没有发生”。

还要确认服务是否运行在内存受限的 cgroup 中:

PID=1234
cat "/proc/$PID/cgroup"

if [ -f /sys/fs/cgroup/memory.current ]; then
    cat /sys/fs/cgroup/memory.current
    cat /sys/fs/cgroup/memory.max
    cat /sys/fs/cgroup/memory.events
fi

上述 memory.* 文件主要对应 cgroup v2,并且必须在目标服务所属的 cgroup 中读取。直接读取当前 Shell 所在的 cgroup,可能得到的是 Shell 的限制,而不是目标服务的限制。

memory.events 中的 oom 或 oom_kill 增加,说明该 cgroup 曾受到内存不足处置。即使主机还有可用内存,服务也可能因为自身的 memory.max 达到上限而被限制或杀死。容器环境同样需要区分容器限制和宿主机整体余量。

按低风险顺序排查内存异常

第一步:固定观察范围和负载

先记录服务器总内存、可用内存、Swap、主要进程和业务量。至少要知道异常发生时:

  • 是整台香港服务器内存紧张,还是单个服务紧张。
  • 请求量、并发数、批处理规模是否发生变化。
  • 进程是否刚重启、刚完成配置变更或刚执行过缓存预热。
  • 异常是持续增长,还是高峰过后能够回落。

没有相同负载下的对照,单纯比较两个时间点很容易得出错误结论。

第二步:区分文件缓存、内核占用和匿名内存

查看 /proc/meminfo 时,不要只看 Cached,还应同时比较:

  • AnonPages:用户进程匿名内存的大致规模。
  • Cached:文件页缓存。
  • SReclaimable:可回收 slab 的一部分。
  • SUnreclaim:不容易直接回收的 slab。
  • Shmem:共享内存、tmpfs 等相关占用。
  • SwapFree:交换空间余量。

如果匿名内存随业务量持续增长,而文件缓存变化不大,应优先定位进程。若 SUnreclaim 快速增长,问题可能在内核对象、驱动、文件系统或某类内核资源,不能简单归咎于应用堆泄漏。

需要查看 slab 时,可以尝试:

slabtop -o

slabtop 不是所有系统默认安装,且不同发行版支持的选项可能不同。它适合辅助判断 slab 是否异常增长,不足以直接指认具体业务进程。

第三步:定位进程并记录 PSS 趋势

先用 RSS 找出候选进程,再用 smaps_rollup 或进程状态文件确认匿名、文件和共享内存的组成。对多个候选进程进行连续采样,而不是只跟踪当前排名第一的进程。

若某个进程的 RssAnon 和 PSS 持续增长,且负载回落后仍不下降,应查看该服务的:

  • 应用缓存是否有上限。
  • 连接、线程和队列是否持续累积。
  • 垃圾回收或内存分配器是否存在延迟回收。
  • 最近是否修改了批量大小、并发数或超时配置。
  • 是否有未关闭的文件、连接、订阅或请求上下文。

这些检查需要结合具体运行时和应用日志完成,不能仅凭 Linux 进程表确定代码层面的泄漏位置。

第四步:验证修复,而不是只验证重启

如果必须重启服务作为临时止血措施,应先保留异常期间的指标、日志和进程信息,并确认重启会影响哪些请求、连接和任务。生产环境执行 systemctl restart、容器重建或进程终止前,应准备维护窗口、服务恢复方式和配置回滚方案;不要对无法确认归属的高占用 PID 直接使用 kill -9。

重启后的内存下降只能作为“释放成功”的验证。真正的修复验证应是:

  1. 在相近的业务负载下重新运行相同类型任务。
  2. 连续记录整机 MemAvailable、Swap 活动和目标进程 PSS。
  3. 观察任务结束后内存是否回落到可接受的基线附近。
  4. 检查 OOM 日志、cgroup memory.events 和业务延迟是否不再增长。
  5. 若仍按类似速度增长,说明问题尚未解决,需继续检查应用缓存、分配器、运行时或输入规模。

如何作出处理边界

可以按以下条件区分“暂时不用处理”和“需要立即定位”:

观察结果更可能的原因处理方向
free 下降,但 MemAvailable 稳定,si/so 基本没有活动文件缓存或可回收内存增加继续观察,不要仅凭 free 清缓存
Cached 增长,负载结束后趋稳,业务延迟正常文件访问形成 page cache以可用内存和延迟为准评估
某进程 RSS/PSS 随时间单调增长,RssAnon 同步增加应用缓存、堆增长、运行时保留或疑似泄漏固定负载复测并结合应用指标定位
Swap 已使用但 si/so 长期接近零历史页面被换出,当前压力未必持续结合 MemAvailable、延迟和后续趋势判断
si/so 持续活动且业务延迟升高工作集超过可快速驻留内存降低并发或任务规模,并定位占用来源
主机还有内存,但 cgroup memory.max 达到上限服务级内存限制检查限制配置与实际工作集,不能只看主机总量
出现 oom-kill 或 memory.events 中 oom_kill 增加已发生内存不足处置先保留证据,再修复增长来源或重新评估容量

容量判断不应使用一个脱离业务的固定百分比。更可靠的方法是,在可预测的峰值负载下测量进程实际工作集、内核开销、必要的文件缓存、并发突发和维护操作所需的余量。若只是缓存增加且 MemAvailable、换页活动和延迟保持稳定,通常不需要因为 free 变小就扩容;若匿名工作集持续增长、Swap 活动频繁、cgroup 反复触顶或已经出现 OOM,则应先修复占用异常,再根据复测峰值评估是否需要增加内存或调整服务并发。

对香港服务器进行最终判断时,至少应保留一段包含正常负载、峰值负载和负载回落的时间序列。只有在相同条件下复测后,才能区分一次性的缓存预热、正常工作集扩大与会反复增长的进程内存问题。

目录结构
全文