先说结论:内存占用高不一定是故障,Linux 会把空闲内存拿去做缓存;真正要盯的是 available、swap 读写和 OOM。先分清“缓存占着”还是“进程真的漏了”,再决定重启、调配置还是加内存。
三个数先看明白:
- free:完全没用的内存,通常很小,正常
- available:应用还能拿到的内存,这个才是关键
- buff/cache:缓存,可回收,不要看到它高就慌
如果 available 还充足、swap 没有持续读写,内存“看起来高”通常不是问题。反过来,available 很低、si/so 持续非 0、dmesg 有 OOM,才是真问题。
第一步:看整体内存
free -m
vmstat 1 5
# 看 si/so 是否持续非 0
cat /proc/meminfo | grep -E 'MemAvailable|SwapTotal|SwapFree|Dirty'
MemAvailable 是内核估算的可用内存,比 free 更有参考价值。buff/cache 高但 available 正常,说明缓存在干活,不需要处理。
第二步:找进程
ps aux --sort=-rss | head -20
top -o %MEM
smem -r -k 2>/dev/null | head -20
重点看 RSS 和增长趋势。PHP-FPM、MySQL、Java、Node、Redis 是常见的内存大户。把每个进程的实际占用和配置对上:FPM 的 pm.max_children、MySQL 的 buffer pool、Java 的 -Xmx、Redis 的 maxmemory。

第三步:判断是不是泄漏
泄漏的典型特征是“重启后回落,跑一段时间又涨回去”。判断方法:
- 记录进程 RSS,每小时一次,画一条曲线
- 观察业务量没变时 RSS 是否持续上升
- 重启单个 worker,看内存是否明显回落
- Java 抓 heap dump,PHP/Node 看内存 profile
如果 RSS 随请求量波动、稳定在某个区间,多半是缓存或连接池,不是泄漏。
第四步:看 OOM 和 cgroup
dmesg | grep -i -E 'oom|killed process'
journalctl -k | grep -i oom
cat /sys/fs/cgroup/memory.max 2>/dev/null
cat /sys/fs/cgroup/memory.current 2>/dev/null
容器里被杀,常见原因是 cgroup 限制而不是宿主机没内存。看 memory.max 和 memory.current,再看 OOM 日志里的进程名和触发时的内存。

第五步:swap 要不要开
swap 不是洪水猛兽,但要看场景:
- 数据库、Redis:尽量不开,或把 swappiness 调低,避免热数据被换出
- Web/应用:可以开小一点,作为 OOM 前的缓冲
- 容器:注意 cgroup 的 swap 限制,别让容器把宿主机拖死
sysctl vm.swappiness
# 临时调整
sysctl -w vm.swappiness=10
swap 只能续命,不能解决泄漏。si/so 持续很高,说明内存已经不够,性能也会明显下降。
第六步:常见原因
- PHP-FPM worker 太多:每个 worker 几十 MB,max_children 一调就爆
- MySQL buffer pool 过大:占了机器一半以上,系统和其他进程没空间
- Java 堆没限制:默认堆按物理内存比例走,容器里容易超
- Redis 没设 maxmemory:数据一直涨,最后被 OOM
- 应用泄漏:长连接、缓存、日志缓冲没清理
- 备份/导出任务:一次性把大文件读进内存
连接池和缓存也会推高内存。数据库连接池、Redis 客户端、HTTP 客户端连接池,每个连接都占内存;池子开太大,空闲连接也会把内存吃掉。按实际并发设上限,别用默认值。
容器里还要看 cgroup 的 memory.max 和 memory.current。宿主机内存很足,但容器限制设得小,一样会 OOM;反过来,容器没限制,一个进程泄漏就能把整台机器拖死。
另外,RSS 高不等于泄漏,虚拟内存(VSZ)高更不代表问题;看 RSS 和实际可用内存,别被 VSZ 吓到。
第七步:应急处理
- 先看 available 和 OOM,确认是不是真的要处理
- 重启单个 worker 或 FPM pool,不要直接 reboot
- 限流、降级非核心功能,减少内存分配
- 临时加 swap 或升配,争取时间
- 保留现场:RSS 曲线、OOM 日志、heap dump
第八步:长期优化
- 按进程算内存预算:系统 + 应用 + 数据库 + 缓存 + 余量
- 给容器设 memory limit 和 request,避免互相抢
- FPM max_children、Java -Xmx、Redis maxmemory 都要显式配置
- 监控 available、swap、OOM、进程 RSS 趋势
- 修复泄漏,别用定时重启掩盖问题

常见问题 FAQ
Q1:内存占用 90% 一定要处理吗?
不一定。看 available 和 swap。缓存占着内存是正常的,available 充足就不用动。
Q2:怎么快速判断是不是泄漏?
业务量不变时 RSS 持续上升,重启后回落,基本就是泄漏。再用 heap dump 或 profile 确认。
Q3:OOM 是内存不够还是限制太紧?
看 dmesg 和 cgroup 限制。容器里常见是 memory.max 设得太小,不一定是宿主机没内存。
Q4:加内存能解决问题吗?
容量问题可以,泄漏问题只能推迟。先定位是配置、容量还是代码,再决定加不加。
Q5:swap 开了会不会更慢?
频繁换页会明显变慢。数据库和 Redis 尽量少用 swap,Web 应用可以留一点作为缓冲。
总结
内存排查的顺序是:看 available 和 swap → 找进程 → 判断泄漏 → 看 OOM/cgroup → 调配置或加内存。别被 buff/cache 吓到,也别用定时重启掩盖泄漏。
需要一台内存更充裕的机器做对照测试,可以按小时开一台萤光云香港 CN2 VPS,确认是容量还是代码问题。







