LHIDC

linux内存占用过高不一定是泄漏,缓存、进程和OOM如何区分

本文面向初学者说明 Linux 内存占用高时的判断方法,重点解释 available、buff/cache、Swap、RSS/PSS 与 OOM 日志,帮助区分正常缓存、进程内存增长和真实内存风险。

linux内存占用过高不一定是泄漏,缓存、进程和OOM如何区分

先划清边界:Linux 内存“占满”不等于内存泄漏

一次常见的故障复盘是这样开始的:监控显示 linux 服务器内存占用过高,freeused 接近总内存,业务人员担心应用发生内存泄漏,于是准备重启服务。可登录服务器后发现 buff/cache 很大,available 也不低,系统没有明显卡顿,日志里也没有 OOM。这种情况通常不是故障,而是 Linux 把暂时不用的内存拿去做页缓存,提高文件读写性能。

判断 linux 内存占用过高时,核心不是只看 used,而是区分三件事:如果 buff/cache 高但 available 仍然充足,通常属于正常缓存;如果某个进程的 RSS/PSS 持续上升,并且占用无法回落,才需要重点排查进程内存增长;如果 available 很低、Swap 频繁进出、系统出现回收停顿,或者日志中出现 Out of memoryKilled process,才说明已经存在 OOM 风险或发生过 OOM。

free 输出里最容易误读的是 used、available 和 buff/cache

先看一个典型输出:

free -h
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       2.1Gi       380Mi       120Mi       5.2Gi       5.1Gi
Swap:          2.0Gi          0B       2.0Gi

很多初学者会盯着 free 很小,认为系统快没内存了。但在 Linux 中,free 只表示完全空闲、暂时没有被使用的内存。Linux 的设计并不追求让内存一直空着,而是尽量把空闲内存用于文件缓存、目录项缓存、inode 缓存等,以便下次访问磁盘文件时更快。

几个字段可以这样理解:

字段 含义 排查时怎么看
used 已使用内存的概览值,通常包含进程占用,也会受到缓存统计影响 不适合作为唯一告警依据
free 完全空闲的内存 低不一定有问题
buff/cache 缓冲区和页缓存等,大部分用于提升 I/O 性能 高不一定是故障,要结合 available
available 系统估算的“在不严重影响运行的情况下可供新程序使用的内存” 判断内存压力时优先看它
Swap 交换分区或交换文件使用情况 持续 si/so 表示压力较大

availablefree 更有参考价值。它不是简单的空闲内存,而是内核结合可回收缓存、部分 slab、当前内存水位等因素估算出来的可用空间。也就是说,buff/cache 中有相当一部分可以在应用需要内存时被回收,但不是所有缓存都能立刻、无成本地释放。

需要注意,不同 Linux 内核版本、发行版和 procps 工具版本对字段展示会略有差异。较老的系统可能没有 available 字段,或者 buff/cache 的统计方式和新系统不完全一致。遇到老版本系统时,应结合 /proc/meminfovmstat、Swap 使用和实际业务现象一起判断。

可以进一步查看内核的原始统计:

cat /proc/meminfo | egrep 'MemTotal|MemFree|MemAvailable|Buffers|Cached|SReclaimable|Slab|SwapTotal|SwapFree'

其中 CachedBuffersSReclaimable 等都可能影响你在 free 中看到的 buff/cache

正常缓存、进程增长和 OOM 风险的判断边界

正常页缓存:看起来占用高,但系统仍有余量

如果服务器主要做文件读写、日志处理、备份、镜像构建、数据库读取或静态资源服务,buff/cache 增长很常见。它的典型特征是:

  • free 较低,但 available 仍然较高;
  • buff/cache 占比大;
  • Swap 没有明显使用,或者没有持续换入换出;
  • 业务响应正常,系统负载没有异常升高;
  • OOM 日志中没有进程被杀;
  • 当新进程需要内存时,缓存会被内核回收一部分。

这种情况下,不建议把缓存占用直接视为故障,也不建议把清理缓存作为常规运维动作。手动执行 drop_caches 可能让监控曲线暂时变好看,但会破坏缓存命中,导致后续磁盘 I/O 增加,反而影响业务判断。除非是在明确的测试场景中需要排除缓存干扰,否则不应把它当成修复手段。

进程内存增长:关注 RSS/PSS 的趋势,而不是单次截图

进程内存增长和缓存不同,它通常表现为某个或某类进程长期占用越来越多物理内存。排查时需要关注趋势,而不是只看某一刻的数值。

常见判断信号包括:

  • 某个进程的 RSS 长时间持续上升;
  • buff/cache 不高,但 used 中主要由进程占用;
  • 重启该进程后内存明显释放,运行一段时间后再次增长;
  • 多个同类 worker 或子进程同时增长;
  • 容器或 cgroup 的内存使用接近限制值;
  • 应用日志中伴随频繁 GC、连接堆积、任务积压等现象。

这里要注意 VSZRSS 的区别。VSZ 是进程的虚拟地址空间,不等于实际占用的物理内存;某些 Java、Go、数据库或使用 mmap 的程序,VSZ 很大并不一定表示内存真的被吃掉。排查实际物理占用时,优先看 RSS,如果有工具支持,再看更准确的 PSS

OOM 风险:不只是“内存快满”,而是分配失败或回收困难

OOM 是 Out Of Memory 的缩写,表示内核在分配内存时无法满足请求,并且通过回收缓存、回收匿名页、使用 Swap 等方式仍然无法缓解,最终可能触发 OOM Killer 杀掉某个进程。

OOM 风险通常有这些信号:

  • MemAvailable 长时间处于很低水平;
  • Swap 使用持续增加,并出现频繁换入换出;
  • vmstatsiso 不为 0 且持续出现;
  • 系统响应变慢,kswapd CPU 占用升高;
  • /proc/pressure/memory 显示内存压力明显;
  • 日志中出现 Out of memoryoom-killerKilled process
  • 容器状态显示 OOMKilled,但宿主机内存看起来仍有剩余。

容器场景尤其容易误判。宿主机还有可用内存,不代表容器不会 OOM。容器如果设置了内存上限,进程触达 cgroup 限制时就可能被杀掉。

影响判断的几个因素

Linux 内存统计不是一个单一数字,下面这些因素都会影响判断结果。

文件缓存和目录项缓存会主动占用空闲内存

服务器读写文件越频繁,页缓存越容易增长。例如日志扫描、备份、解压缩、对象存储同步、镜像构建等操作,都可能让 buff/cache 快速变大。只要 available 仍然健康,且没有 Swap 抖动,这类增长通常是系统优化行为。

tmpfs 和共享内存可能藏在“看似缓存”的位置

/dev/shm、tmpfs 挂载点、某些数据库共享内存、容器共享内存设置,都可能让内存消耗看起来不直观。可以检查 tmpfs 使用情况:

df -h -t tmpfs

如果某个 tmpfs 挂载点接近占满,需要判断它是否存放了临时文件、上传缓存、运行时 socket 或应用中间结果。

slab 缓存过高需要单独观察

内核 slab 用于缓存目录项、inode、网络对象等。部分 slab 是可回收的,部分不可回收。如果 SlabSUnreclaim 异常高,普通的进程列表可能看不到对应的大户。

可以查看:

cat /proc/meminfo | egrep 'Slab|SReclaimable|SUnreclaim'

进一步分析 slab 时可使用 slabtop

slabtop

如果是目录项、inode 缓存较高,通常和大量小文件访问有关;如果不可回收 slab 异常增长,则需要结合内核版本、驱动、文件系统或网络连接状态继续分析。

Swap 不是有使用就等于故障,但频繁换入换出需要警惕

Swap 被使用过并不一定代表当前有问题。有些系统曾经在内存紧张时把冷数据换出,之后即使内存恢复,Swap 也不会立刻归零。真正需要关注的是是否持续发生换入换出。

查看实时变化:

vmstat 1 5

重点看:

  • si:swap in,从 Swap 读回内存;
  • so:swap out,把内存写入 Swap;
  • free:空闲内存;
  • wa:I/O 等待;
  • rb:运行队列和不可中断等待。

如果 siso 持续出现,并且业务延迟升高,说明系统已经在用磁盘弥补内存不足,性能风险会明显增加。

按顺序验证:先看整体,再看进程,最后查 OOM 证据

1. 先确认系统是否真的有内存压力

建议先执行:

free -h
vmstat 1 5
cat /proc/meminfo | egrep 'MemAvailable|MemFree|Buffers|Cached|SReclaimable|SUnreclaim|SwapTotal|SwapFree'

判断时可以按这个顺序:

  1. available 是否仍然充足;
  2. buff/cache 是否占了较大比例;
  3. Swap 是否只是“有使用”,还是正在持续换入换出;
  4. 系统是否有响应变慢、I/O 等待升高、负载异常;
  5. 是否存在 OOM 日志。

如果系统支持 PSI,可以查看内存压力:

cat /proc/pressure/memory

输出类似:

some avg10=0.00 avg60=0.05 avg300=0.02 total=123456
full avg10=0.00 avg60=0.00 avg300=0.00 total=0

some 表示部分任务因内存回收而等待,full 表示所有非 idle 任务都在等待内存资源。该接口依赖内核版本,并不是所有系统都有。

2. 定位高内存进程

如果判断不是单纯缓存,就要定位进程。常用命令如下:

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

字段含义:

  • PID:进程 ID;
  • PPID:父进程 ID;
  • USER:进程所属用户;
  • COMM:进程名;
  • %MEM:占总内存比例;
  • RSS:常驻物理内存,单位通常为 KB;
  • VSZ:虚拟内存大小,不等于真实物理占用。

也可以用 top,进入后按 Shift + M,按内存占用排序。

如果需要观察某个进程的详细映射:

pmap -x <PID> | tail -n 20

pmap 对大型进程可能需要一定时间,生产环境中应避免在系统已经严重卡顿时频繁执行。

如果系统安装了 smem,可以用 PSS 更准确地估算共享库、共享内存的分摊占用:

smem -r -k -t | head -n 20

没有 smem 时,也可以先用 pstop、应用自身监控和启动参数进行初步判断。

3. 不要忽略 systemd、cgroup 和容器限制

在容器或 systemd 服务隔离场景下,宿主机层面的 free 只能说明整机情况,不能说明某个服务或容器是否接近限制。

查看 systemd cgroup 资源占用:

systemd-cgtop

Docker 场景可以查看:

docker stats

检查容器是否曾经被 OOM 杀掉:

docker inspect --format='OOMKilled={{.State.OOMKilled}} ExitCode={{.State.ExitCode}}' <container_name_or_id>

如果看到 OOMKilled=true,说明容器内部已经触发过 OOM。即使宿主机还有内存,也需要检查容器内存限制、应用峰值、并发量和缓存策略。

4. 查找 OOM 日志,确认是否发生过进程被杀

Linux 发生 OOM 时,内核通常会记录日志。使用 systemd 的系统可以查:

journalctl -k -g 'Out of memory|oom|Killed process' --since '24 hours ago'

也可以查看内核环形日志:

dmesg -T | egrep -i 'out of memory|oom-killer|killed process'

不同发行版日志位置不同。常见位置包括:

  • Debian/Ubuntu:/var/log/kern.logjournalctl -k
  • CentOS/RHEL:/var/log/messagesjournalctl -k
  • 容器平台:还要结合容器运行时、编排平台事件和容器退出码。

典型 OOM 日志可能包含:

Out of memory: Killed process 12345 (java) total-vm:4194304kB, anon-rss:2097152kB, file-rss:0kB, shmem-rss:0kB

这里可以看到被杀的进程、进程名、虚拟内存、匿名页 RSS 等信息。但要注意,被 OOM Killer 杀掉的进程不一定就是“真正的罪魁祸首”。内核会根据 oom_scoreoom_score_adj、内存占用等因素选择牺牲对象。有时一个关键服务被杀,只是因为它分数高或占用大,根因可能是其他进程、容器限制或短时间流量峰值。

可以查看某个进程当前的 OOM 分数:

cat /proc/<PID>/oom_score
cat /proc/<PID>/oom_score_adj

oom_score_adj 越高,越容易在 OOM 时被选中;越低,则越不容易被杀。不要随意把业务进程都调成极低分数,否则可能导致系统在内存耗尽时没有合适对象可杀,风险更大。

用一张表快速区分三类情况

现象 更可能的类型 关键验证点 建议动作
used 高,buff/cache 高,available 也高 正常缓存 无持续 Swap,业务正常,无 OOM 日志 继续观察,不要急于清缓存
available 低,si/so 持续出现,系统变慢 内存压力或 OOM 风险 vmstat、PSI、Swap、负载、日志 找出高内存进程,评估扩容或限制
某个进程 RSS 持续上升 进程内存增长 ps/top/smem 趋势,重启后是否回落 定位进程和业务场景,不在系统层面盲目处理
容器退出码 137 或 OOMKilled=true 容器 OOM 容器限制、cgroup 使用量、容器事件 调整限制或降低峰值占用
OOM 日志出现 Killed process 已发生 OOM journalctl -kdmesg、应用退出时间 对齐日志时间线,确认被杀进程和触发前状态
Slab/SUnreclaim 异常高 内核对象占用异常 /proc/meminfoslabtop 结合文件系统、网络连接、内核版本继续分析

避免几个常见误判

看到 linux 内存占用过高时,最容易犯的错误有三个。

第一个是把 free 小等同于没内存。Linux 本来就会尽量利用空闲内存做缓存,free 低但 available 高,通常不是故障。

第二个是把 buff/cache 高等同于泄漏。缓存是内核管理的资源,很多情况下可以被回收。真正需要警惕的是回收后仍然不够、Swap 持续抖动、进程 RSS 不断增长或 OOM 日志出现。

第三个是只看宿主机,不看容器限制。容器被限制在 1GB 内存时,宿主机剩余 20GB 也不能直接说明容器安全。排查容器 OOM 必须看 cgroup、容器状态和容器内进程占用。

还有一个细节:监控平台的“内存使用率”算法不一定相同。有的监控把 buff/cache 算作已用,有的会按 available 计算可用比例。因此不同监控面板看到的百分比可能不同,排障时最好回到服务器上用命令确认原始数据。

把判断落到可验证证据上

一次可靠的内存排查,至少要能回答下面几个问题:

  • MemAvailable 是否持续偏低?
  • buff/cache 高是否伴随业务异常?
  • Swap 是静态占用,还是持续换入换出?
  • 是否有单个进程或一组进程 RSS/PSS 持续增长?
  • 是否发生过 Out of memoryKilled process
  • 如果是容器,是否触发了 cgroup 内存上限?
  • 当前现象是瞬时峰值,还是稳定复现?

当这些证据都指向缓存时,不必把缓存占用当作故障;当证据指向进程增长时,应围绕具体服务、运行参数和业务峰值继续分析;当证据指向 OOM 风险时,优先保护业务可用性,减少并发峰值、限制异常进程、评估内存扩容或调整容器限制。Linux 内存问题的边界并不在“used 是否很高”,而在系统是否还有可回收空间、进程是否持续吞噬内存,以及内核是否已经无法完成正常分配。

上一篇 海外服务器部署Next.js SSR站点,PM2、Nginx反向代理与HTTPS配置如何配合 下一篇 香港节点网站访问速度慢,性能测试应同时看TTFB、带宽与应用耗时

LHIDC 产品中心

继续查看可购买的海外服务器产品

文章用于辅助选型,最终价格、库存与配置请以产品详情页和下单页面展示为准。

查看产品 查看方案