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

先划清边界:Linux 内存“占满”不等于内存泄漏
一次常见的故障复盘是这样开始的:监控显示 linux 服务器内存占用过高,free 里 used 接近总内存,业务人员担心应用发生内存泄漏,于是准备重启服务。可登录服务器后发现 buff/cache 很大,available 也不低,系统没有明显卡顿,日志里也没有 OOM。这种情况通常不是故障,而是 Linux 把暂时不用的内存拿去做页缓存,提高文件读写性能。
判断 linux 内存占用过高时,核心不是只看 used,而是区分三件事:如果 buff/cache 高但 available 仍然充足,通常属于正常缓存;如果某个进程的 RSS/PSS 持续上升,并且占用无法回落,才需要重点排查进程内存增长;如果 available 很低、Swap 频繁进出、系统出现回收停顿,或者日志中出现 Out of memory、Killed 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 表示压力较大 |
available 比 free 更有参考价值。它不是简单的空闲内存,而是内核结合可回收缓存、部分 slab、当前内存水位等因素估算出来的可用空间。也就是说,buff/cache 中有相当一部分可以在应用需要内存时被回收,但不是所有缓存都能立刻、无成本地释放。
需要注意,不同 Linux 内核版本、发行版和 procps 工具版本对字段展示会略有差异。较老的系统可能没有 available 字段,或者 buff/cache 的统计方式和新系统不完全一致。遇到老版本系统时,应结合 /proc/meminfo、vmstat、Swap 使用和实际业务现象一起判断。
可以进一步查看内核的原始统计:
cat /proc/meminfo | egrep 'MemTotal|MemFree|MemAvailable|Buffers|Cached|SReclaimable|Slab|SwapTotal|SwapFree'
其中 Cached、Buffers、SReclaimable 等都可能影响你在 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、连接堆积、任务积压等现象。
这里要注意 VSZ 和 RSS 的区别。VSZ 是进程的虚拟地址空间,不等于实际占用的物理内存;某些 Java、Go、数据库或使用 mmap 的程序,VSZ 很大并不一定表示内存真的被吃掉。排查实际物理占用时,优先看 RSS,如果有工具支持,再看更准确的 PSS。
OOM 风险:不只是“内存快满”,而是分配失败或回收困难
OOM 是 Out Of Memory 的缩写,表示内核在分配内存时无法满足请求,并且通过回收缓存、回收匿名页、使用 Swap 等方式仍然无法缓解,最终可能触发 OOM Killer 杀掉某个进程。
OOM 风险通常有这些信号:
MemAvailable长时间处于很低水平;- Swap 使用持续增加,并出现频繁换入换出;
vmstat中si、so不为 0 且持续出现;- 系统响应变慢,
kswapdCPU 占用升高; /proc/pressure/memory显示内存压力明显;- 日志中出现
Out of memory、oom-killer、Killed process; - 容器状态显示
OOMKilled,但宿主机内存看起来仍有剩余。
容器场景尤其容易误判。宿主机还有可用内存,不代表容器不会 OOM。容器如果设置了内存上限,进程触达 cgroup 限制时就可能被杀掉。
影响判断的几个因素
Linux 内存统计不是一个单一数字,下面这些因素都会影响判断结果。
文件缓存和目录项缓存会主动占用空闲内存
服务器读写文件越频繁,页缓存越容易增长。例如日志扫描、备份、解压缩、对象存储同步、镜像构建等操作,都可能让 buff/cache 快速变大。只要 available 仍然健康,且没有 Swap 抖动,这类增长通常是系统优化行为。
tmpfs 和共享内存可能藏在“看似缓存”的位置
/dev/shm、tmpfs 挂载点、某些数据库共享内存、容器共享内存设置,都可能让内存消耗看起来不直观。可以检查 tmpfs 使用情况:
df -h -t tmpfs
如果某个 tmpfs 挂载点接近占满,需要判断它是否存放了临时文件、上传缓存、运行时 socket 或应用中间结果。
slab 缓存过高需要单独观察
内核 slab 用于缓存目录项、inode、网络对象等。部分 slab 是可回收的,部分不可回收。如果 Slab 或 SUnreclaim 异常高,普通的进程列表可能看不到对应的大户。
可以查看:
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 等待;r、b:运行队列和不可中断等待。
如果 si、so 持续出现,并且业务延迟升高,说明系统已经在用磁盘弥补内存不足,性能风险会明显增加。
按顺序验证:先看整体,再看进程,最后查 OOM 证据
1. 先确认系统是否真的有内存压力
建议先执行:
free -h
vmstat 1 5
cat /proc/meminfo | egrep 'MemAvailable|MemFree|Buffers|Cached|SReclaimable|SUnreclaim|SwapTotal|SwapFree'
判断时可以按这个顺序:
available是否仍然充足;buff/cache是否占了较大比例;- Swap 是否只是“有使用”,还是正在持续换入换出;
- 系统是否有响应变慢、I/O 等待升高、负载异常;
- 是否存在 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 时,也可以先用 ps、top、应用自身监控和启动参数进行初步判断。
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.log、journalctl -k; - CentOS/RHEL:
/var/log/messages、journalctl -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_score、oom_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 -k、dmesg、应用退出时间 |
对齐日志时间线,确认被杀进程和触发前状态 |
Slab/SUnreclaim 异常高 |
内核对象占用异常 | /proc/meminfo、slabtop |
结合文件系统、网络连接、内核版本继续分析 |
避免几个常见误判
看到 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 memory或Killed process? - 如果是容器,是否触发了 cgroup 内存上限?
- 当前现象是瞬时峰值,还是稳定复现?
当这些证据都指向缓存时,不必把缓存占用当作故障;当证据指向进程增长时,应围绕具体服务、运行参数和业务峰值继续分析;当证据指向 OOM 风险时,优先保护业务可用性,减少并发峰值、限制异常进程、评估内存扩容或调整容器限制。Linux 内存问题的边界并不在“used 是否很高”,而在系统是否还有可回收空间、进程是否持续吞噬内存,以及内核是否已经无法完成正常分配。