系统内存占用过高怎么办:从进程定位到缓存释放的排查方案

系统内存占用过高怎么办:从进程定位到缓存释放的排查方案

系统优化wl5net2026-11-15 19:16:37A+A-
约 5706 字 · 预计阅读 12 分钟

遇到系统内存占用过高怎么办?别急着重启机器,更不要上来就用脚本强行清理缓存或者下载所谓的“一键内存优化大师”,正确的处理思路是:先区分“真内存耗尽”还是“系统缓存借用”,再按“查真实可用内存 → 抓高占用进程 → 揪内核与隐藏占用 → 限制或释放异常资源”四步走,绝大多数内存告警都能直接定位根源并止血。

系统内存占用过高怎么办:从进程定位到缓存释放的排查方案

很多运维新手或者电脑用户一看到内存占用飙红就慌,动不动就重启服务甚至硬重启机器,结果不仅没查出内存泄漏的原因,还把业务现场破坏了,过不了多久问题照样复发。下面直接把一线排查和处理的实操方案拆开讲透。


核心方案速览

不管你面对的是 Linux 生产服务器,还是 Windows 本地工作机,先用对应的一步命令或工具判断当前到底属于哪种情况:

  • Linux 服务器快速定性:先敲 free -h,重点盯住 available 这一列。如果 available 依然充足,只是 buff/cache 高,说明系统正在拿空闲内存做磁盘缓存,属于健康状态,不需要手动干预;如果 available 见底且开始狂吃 Swap,立刻用 ps aux --sort=-%mem | head -n 15 抓出头号进程。
  • 隐藏内存黑洞定位:如果 top 里所有进程加起来内存并不高,但系统内存依然爆满,重点查两处——内核 Slab 缓存占用(slabtop)和已被删除但进程未释放句柄的文件(lsof +L1),以及 tmpfs 内存盘是否塞满了临时文件。
  • Windows 设备排查:别只看任务管理器里的百分比,直接按 Win + R 输入 resmon 打开资源监视器看“硬错误”和工作集;如果进程加起来对不上总占用,直接用微软官方的 RAMMap 查是不是驱动导致了“非分页缓冲池(Nonpaged Pool)”内存泄漏。
  • 应急止血手段:给核心进程设置 OOM 保护优先级避免被误杀;对失控的业务进程通过 systemd 或 Docker 限制内存上限;临时挂载 Swap 空间兜底,防止系统直接卡死。

实操细节深度拆解

1. 先看懂到底是不是“真内存不足”(Linux 场景)

很多时候所谓的内存告急只是误判。登录终端先执行:

free -h

你会看到类似下面的输出:

              total        used        free      shared  buff/cache   available
Mem:           15Gi       4.2Gi       620Mi       180Mi        10Gi        10Gi
Swap:         2.0Gi          0B       2.0Gi

避坑点:

  • 别只看 free 列:上面示例里 free 只剩 620Mi,看起来快满了,但实际上 available 还有 10Gi。Linux 的机制是“内存空闲着也是浪费”,会主动把没用到的内存拿去缓存磁盘读写(即 buff/cache)。一旦应用程序真需要内存,内核会立刻把这部分缓存回收让出来。
  • 什么时候才算真高:当 available 接近总内存的警戒线以下,同时 Swap 的 used 持续往上涨,系统响应明显变慢、SSH 敲命令都卡顿,这才是真正需要立刻介入处理的高危状态。

2. 精准抓出吃内存的“元凶”进程

确认是真缺内存后,直接按物理内存占用从高到低拉出前 15 个进程:

ps aux --sort=-%mem | head -n 15

或者直接用 top 命令,进入界面后按一下大写字母 M(即 Shift + m),列表就会按内存占用实时排序。

看 top 时主要盯住两个指标:

  • RES(常驻内存):进程真实占用的物理内存大小,排查看它就够了。
  • VIRT(虚拟内存):进程申请的虚拟地址空间,很多语言或数据库喜欢先“圈地”再慢慢用,看它容易被吓到。

常见高占用场景与直接可用的止血配置:

  1. Java 应用吃光内存:经常遇到开发打包时没限制 JVM 堆内存,默认按宿主机比例去吃,或者直接发生内存泄漏。

    • 操作步骤:检查启动参数,明确锁死堆上限和元空间上限,并加上 OOM 自动导出堆快照方便后续排查:
      java -Xms2g -Xmx2g -XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/java_oom.hprof -jar app.jar
  2. PHP-FPM / Python Worker 进程泛滥:单进程看着只有几十 MB,但子进程开了一堆,加起来直接把机器挤爆。

    • 操作步骤:如果是 PHP-FPM,打开配置文件(通常在 /etc/php/*/fpm/pool.d/www.conf),根据机器总内存调小最大子进程数:
      pm = dynamic
      pm.max_children = 30
      pm.start_servers = 5
      pm.min_spare_servers = 5
      pm.max_spare_servers = 15
      pm.max_requests = 500
    • 实操经验:pm.max_requests 一定要设一个正整数(比如 500 或 1000),让子进程处理完一定数量的请求后自动重建,能非常有效地兜底第三方扩展引起的缓慢内存泄漏。
  3. 用 Systemd 硬性锁死服务内存上限:不想改业务代码又怕某个非核心服务把整台服务器拖死,直接在服务的 .service 文件里加资源限制(以限制最多用 2G 物理内存为例):

    sudo systemctl edit your-service-name

    在打开的编辑器里写入:

    [Service]
    MemoryHigh=1800M
    MemoryMax=2G

    保存后执行 sudo systemctl daemon-reload && sudo systemctl restart your-service-name。这样哪怕这个服务内存泄漏,也只会被内核限制在 2G 之内,不会把数据库或 SSH 挤死。


3. 进程占用加起来不多,内存却满了?(隐藏黑洞排查)

这是运维排错中最容易懵的场景:ps 和 top 里最大进程也就占了 1G,但 16G 的机器显示用了 15G。这时候别瞎猜,直接按下面三板斧查隐藏占用:

排查 A:未释放文件句柄的“幽灵占用”

很多同学清理磁盘时习惯直接 rm -f 删掉正在疯狂写入的超大日志文件。文件从目录里消失了,但只要写入进程(如 Nginx、Java)没重启,文件句柄还被死死捏在手里,空间和缓存都不会释放。

  • 排查命令:
    lsof +L1 | grep deleted
  • 解决方法:找到对应进程后,平滑重载或重启该进程。以后线上清理正在写入的日志,切记不要用 rm,而是用清空重定向:
    > /var/log/nginx/access.log

排查 B:tmpfs 内存盘被塞爆

Linux 里的 /dev/shm 或 /run 默认挂载的是 tmpfs,它直接把内存当硬盘用。如果业务代码把大量临时文件或上传缓存丢进了这里,内存会被悄悄吃干抹净,而 top 里根本看不出进程占了多少。

  • 排查命令:
    df -h | grep tmpfs
  • 解决方法:进入使用率异常的 tmpfs 目录,清理无用的临时文件,并修改应用配置的临时目录路径到真实磁盘上。

排查 C:内核 Slab 内存占用过高

如果前面两个都正常,执行下面命令看内核内存分布:

cat /proc/meminfo | grep -E "Slab|SReclaimable|SUnreclaim"

如果 Slab 占了几个 G 甚至更多,用 slabtop -s c 按内存排序看具体是哪个内核对象在膨胀。线上最常见的是 dentry(目录项缓存)和 inode_cache 暴涨,通常是因为短时间内频繁遍历海量小文件,或者旧版本 curl/NSS 库存在缓存泄漏。

  • 临时缓解:确认是可回收的 SReclaimable(如 dentry)异常膨胀时,可以定向释放目录项和 inode 缓存(比直接清全部缓存温和):
    sync && echo 2 > /proc/sys/vm/drop_caches

4. Windows 系统内存占用过高的实操排查

如果你遇到的是 Windows 桌面端或 Windows Server 内存居高不下,任务管理器往往会给你错误的引导:

  1. 先抓真实进程与硬错误:按 Ctrl + Shift + Esc 打开任务管理器 -> 切到“性能”选项卡 -> 点击底部的“打开资源监视器” -> 切到“内存”标签。重点看“工作集(KB)”和“提交(KB)”。如果某个软件提交内存极大但物理工作集很小,往往是它申请了大量虚拟内存导致系统压力剧增。
  2. 排查驱动级内存泄漏(RAMMap 神器):如果所有进程加起来只有几个 G,但“已使用”显示占了大半,多半是网卡驱动、显卡驱动、杀毒软件或者虚拟机软件引发了非分页缓冲池(Nonpaged Pool)泄漏。
    • 下载微软官方 Sysinternals 套件里的 RAMMap 并以管理员身份运行。
    • 重点看 Use Counts 页里的 Nonpaged Pool 和 Driver Locked。如果数值异常偏高,回想最近更新过什么驱动(常见于老版本 Killer 网卡驱动或某些国产安全卫士的内核驱动),直接回滚或卸载对应驱动即可根治。
  3. 避坑提醒:哪怕你的电脑有 32G 甚至 64G 内存,也千万不要彻底关闭 Windows 的虚拟内存(分页文件)。很多大型软件和游戏在运行时会检查“提交限制(Commit Limit)”,一旦关闭虚拟内存,物理内存还没用完,软件就会直接闪退报错。设为“系统管理的大小”是最稳妥的。

高频关联问题拓展

Q1:能不能在 Linux 里写个定时任务,每隔一小时自动执行 drop_caches 清理内存?

强烈不建议!这是典型的“治标反伤本”的做法。 很多人网上抄来一行脚本丢进 crontab:

# 千万别在生产环境这么干!
0 * * * * sync; echo 3 > /proc/sys/vm/drop_caches

drop_caches 原本是给内核开发者做性能基准测试前“制造冷启动环境”用的,不是日常内存清理工具。你每小时把页缓存清空一次,MySQL、Redis、Nginx 原本能在内存里毫秒级命中的热点数据和索引文件全被踢掉了,接下来系统只能疯狂回源读磁盘,直接导致磁盘 I/O 飙升、数据库慢查询暴增甚至服务雪崩。如果实在只是排查测试需要临时清一次,用完记得就收手。

Q2:内存真顶不住触发了 OOM,怎么保住 MySQL 等核心服务不被系统强杀?

Linux 内存彻底耗尽时,内核的 OOM Killer 会挑一个占用高且评分高的进程直接 kill -9,数据库这种内存大户经常第一个背锅。

实操保护步骤:

  1. 调低核心进程的 OOM 评分:如果是用 systemd 托管的 MySQL,直接在配置里加上:
    [Service]
    OOMScoreAdjust=-900

    不想重启服务的话,直接查出进程 PID,临时写入调整值(-1000 表示豁免被杀):

    echo -900 > /proc/$(pidof mysqld)/oom_score_adj
  2. 合理配置 Swap 作为缓冲垫:很多云厂商默认镜像不配 Swap,内存一满直接死机。建议哪怕是 SSD 云盘也挂一个 2G~4G 的 Swap 文件用来缓冲突发峰值,并把倾向性调低(只有真快满了才用 Swap):

    # 创建 4G Swap 文件
    sudo fallocate -l 4G /swapfile
    sudo chmod 600 /swapfile
    sudo mkswap /swapfile
    sudo swapon /swapfile
    
    # 写入开机挂载并调低 swappiness
    echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
    sudo sysctl vm.swappiness=10
    echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf

Q3:为什么在 Docker 容器里看内存没满,宿主机却显示快炸了(或者反过来)?

这个问题在容器化部署时极其常见。早期的监控脚本或老版本应用(比如老旧的 JDK、Node.js)在容器里启动时,读取的是 /proc/meminfo,它看到的是宿主机的总内存,而不是容器被限制的配额。结果容器只给了 2G 配额,Java 进程按宿主机 32G 去开内存,刚启动没多久就被宿主机 cgroup 直接 OOM 干掉。

  • 排查命令:在宿主机上看容器真实的实时内存和限制上限,不要进容器敲 free -h,直接用:
    docker stats --no-stream
  • 解决建议:跑容器时务必显式加上 --memory="2g" --memory-swap="2g",同时在容器内应用层面(如 JVM、Go 的 GOMEMLIMIT)显式传入与容器一致的内存上限,别让程序自己瞎猜。

结尾总结

梳理清楚系统内存占用过高怎么办,核心其实就一句话:别盲目杀缓存,先看懂数据再动手。在 Linux 环境下,先通过 free -h 的 available 判断是否真的吃紧,再用 top、ps 抓大头进程,遇到账目对不上时往 lsof +L1、tmpfs 和 slabtop 这三个死角查,配合 systemd 限制、OOM 优先级保护和合理的程序参数调优,基本就能把问题按死在摇篮里;而在 Windows 端,善用资源监视器和 RAMMap 区分普通进程占用与驱动泄漏,比装一堆流氓清理软件靠谱得多。下次遇到内存告警,按这套流程顺着往下捋一遍,很快就能让系统稳下来。

点击这里复制本文地址 以上内容由未来技术网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

网站地图 robots.txt 网站死链 关于本站 工信部备案号浙ICP备2026012592号-2 浙公网安备33102402000669号