内存故障排查宝典
——从“内存爆了”到定位、修复、验证的完整排查手册
目标:让不懂内存的人,遇到“内存爆满、程序越跑越大、系统越来越卡、Out of Memory、页面文件不足、某个程序吃满内存”等问题,也能按步骤排查。
核心思想:不要看到内存 90% 就直接加内存。先判断“到底是谁占的、占的是什么、为什么一直涨、涨到哪里、重启后是否恢复”。
0. 先学会看懂“内存问题”
很多人看到:
“内存 95%”
就认为:
“内存坏了。”
这是最常见的误区。
内存问题至少可以分成下面几类:
| 类型 | 通俗解释 | 常见症状 | 优先排查 |
|---|---|---|---|
| 物理内存不足 | 电脑 RAM 本身不够 | 整机变卡、频繁换页 | 系统/进程 |
| 单进程异常占用 | 某个程序吃掉大量 RAM | 某软件占用几十 GB | 进程 |
| 内存泄漏 | 程序用过的内存不释放,越跑越大 | 刚启动正常,运行几小时越来越大 | 应用 |
| 缓存占用 | 系统把 RAM 当缓存使用 | 已用内存高,但可快速回收 | 系统 |
| 虚拟内存/页面文件问题 | RAM 不够时使用磁盘顶上 | 程序卡顿、页面文件告警 | 系统 |
| 内核/驱动内存问题 | Windows 内核或驱动吃内存 | 普通进程加起来并不高,但总内存很高 | 内核 |
| 提交内存过高 | 系统承诺给程序的虚拟内存快达到上限 | “提交限制”接近极限 | 页面文件/进程 |
| 句柄/GDI 泄漏 | 程序不断创建对象却不释放 | 内存未必特别高,但程序越来越异常 | 应用/GUI对象 |
| 共享/容器/虚拟机限制 | 宿主机或容器给出的内存上限被打满 | 主机看起来正常,应用却 OOM | 虚拟化/容器 |
| 数据结构膨胀 | 程序本身保存了越来越多数据 | GC 频繁、延迟升高、最终 OOM | 应用 |
| 碎片/连续大块内存不足 | 总量看起来够,但某种分配失败 | 特定大对象/内核分配失败 | 应用/内核 |
1. 内存排障的总原则
1.1 先判断“是不是内存真的不够”
先问 4 个问题:
问题 A:是整机慢,还是只有一个程序慢?
- 整机都慢 → 先查系统内存压力
- 只有一个程序慢 → 先查这个进程
- 程序运行时间越长越慢 → 高度怀疑泄漏/缓存增长
- 只有特定操作时瞬时爆 → 高度怀疑峰值分配/大对象
1.2 问题 B:内存是“一直高”,还是“越来越高”?
一直高
可能是:
- 正常缓存
- 常驻大程序
- 数据库/虚拟机本身需要大量内存
- RAM 太少
越来越高
优先怀疑:
- 内存泄漏
- 缓存无限增长
- 队列积压
- 连接/会话对象没有释放
- 文件句柄、GDI、内核对象泄漏
1.3 问题 C:重启后会不会恢复?
重启后恢复,但运行几个小时又爆
强烈怀疑:
软件/服务逐渐积累资源。
这类问题最重要的是:
记录“时间 → 内存 → 进程 → 操作”变化曲线。
1.4 问题 D:任务管理器里的“已用内存”是不是等于“进程加起来”?
不是。
Windows 内存至少要区分:
| |
所以:
“任务管理器把所有进程加起来只有 10 GB,但电脑已经占了 30 GB”
并不一定是数据错了,剩余部分可能来自系统、缓存、内核、压缩内存等。
2. 一张“10 秒定位”决策树
遇到“内存爆了”时,不要马上杀进程。
按顺序:
| |
3. 内存排障统一流程
以后任何内存问题都尽量按照:
| |
4. 第 1 层:物理内存层 —— RAM 到底够不够
4.1 这一层管什么
最简单理解:
电脑真正能直接高速工作的内存容量。
例如:
| |
不代表:
| |
因为系统和硬件本身也会使用一部分。
4.2 典型症状
物理内存不足常见表现
- 整机明显变卡
- 切换窗口很慢
- 磁盘持续高占用
- 程序频繁卡顿
- 浏览器标签切换明显延迟
- 程序提示 Out of memory
- 页面文件使用量明显增加
4.3 Windows 第一检查
图形界面
打开:
| |
重点看:
| |
4.4 命令行
| |
换算:
| |
4.5 怎么判断
情况 A:内存高,但“可用”仍然不少
不要急着判故障。
可能:
- 文件缓存
- Standby
- 系统缓存
情况 B:可用内存很低 + 系统明显卡顿
重点查:
进程 + 页面文件 + 内存压力
情况 C:可用内存持续下降
重点怀疑:
某个程序或服务在持续吃内存。
5. 第 2 层:进程层 —— 到底是谁吃掉了内存
5.1 最常见问题
用户说:
“电脑内存爆了。”
其实真正的问题可能是:
| |
5.2 第一步:任务管理器
| |
先找:
排名最高的 5 个进程。
不要只看第一名。
5.3 PowerShell
查看当前进程工作集:
| |
5.4 正常还是异常?
正常
| |
可疑
| |
这种情况:
高度怀疑内存泄漏。
6. 第 3 层:增长曲线层 —— “是不是泄漏”最关键的一步
6.1 什么是内存泄漏
简单理解:
| |
例如:
| |
这就是典型可疑趋势。
6.2 不是“内存一直高”就等于泄漏
例如:
| |
这不一定是泄漏。
而:
| |
更加值得怀疑。
7. 第 4 层:Windows Working Set / Private Memory / Commit
这是排查内存泄漏时非常重要的一层。
7.1 Working Set
简单理解:
当前实际驻留在物理 RAM 中的那部分内存。
7.2 Private Memory
简单理解:
更适合观察一个进程独占的内存压力。
如果:
| |
比单纯 Working Set 上涨更值得警惕。
7.3 Commit
简单理解:
系统已经“承诺”给程序的虚拟内存总量。
它与物理 RAM 不完全相同。
因此:
“RAM 还有一点,但是 Commit 已经接近上限”
依然可能导致 OOM。
8. 第 5 层:提交内存(Commit)异常
8.1 什么叫 Commit 爆了
系统有:
| |
例如:
| |
意味着:
系统距离“虚拟内存承诺上限”已经很近。
8.2 为什么会这样?
常见:
- RAM 不够
- 页面文件过小
- 单个进程虚拟内存不断增加
- 多个大程序同时运行
- 内存泄漏
8.3 Windows 查看
任务管理器:
| |
同时可看:
| |
然后按“提交大小”排序。
9. 第 6 层:页面文件(Pagefile)问题
9.1 页面文件是什么
可以简单理解为:
RAM 不够时,Windows 在磁盘上准备的一块虚拟内存空间。
它速度远远低于 RAM。
因此:
页面文件不是 RAM 的完全替代品。
9.2 常见症状
- 内存爆满时磁盘疯狂读写
- 切换程序明显卡
- 机器没有完全死机,但响应极慢
- 内存告警
- 程序频繁卡住
9.3 检查
打开:
| |
不要看到页面文件大就认为故障。
真正需要关注:
| |
9.4 修复原则
普通用户:
优先使用“自动管理所有驱动器的分页文件大小”。
不建议:
为了“释放磁盘空间”永久关闭页面文件。
10. 第 7 层:缓存 —— “内存高”不一定有问题
很多系统会主动利用空闲 RAM 做缓存。
例如:
| |
这可能是正常行为。
10.1 判断核心
问:
系统在有需要时能不能快速回收?
如果可以:
通常不能仅凭“已用很高”判定故障。
11. 第 8 层:内存压缩
Windows 可能会使用内存压缩来减少分页压力。
任务管理器:
| |
也可能看到:
| |
不要因为“压缩内存存在”就认为系统有故障。
真正应该关注:
- 是否持续升高
- 系统是否明显卡顿
- Page Fault 是否异常
- Commit 是否接近限制
12. 第 9 层:内核/驱动内存泄漏
这是非常容易被忽略的一类。
典型现象:
| |
这时候不要继续杀普通应用。
需要怀疑:
| |
13. Windows 内核内存重点看什么
任务管理器:
| |
重点:
| |
13.1 非分页池异常
简单理解:
某些必须始终留在 RAM 中的内核内存。
如果:
| |
可能怀疑:
驱动/内核资源泄漏。
13.2 分页池异常
如果:
| |
也需要检查:
- 驱动
- 文件系统
- 安全软件
- 第三方服务
14. PoolMon —— 排查驱动内存泄漏
Windows 可以使用:
| |
核心思路不是“看谁最大”,而是:
| |
观察:
某个 Tag 的内存是否随时间持续增长。
14.1 排查流程
| |
15. 第 10 层:句柄泄漏
某些程序即使 RAM 增长不明显,也可能不断消耗:
| |
长期运行后可能出现:
- 程序越来越慢
- 打不开文件
- 创建对象失败
- UI 异常
- 最后程序崩溃
15.1 查看句柄
任务管理器:
| |
然后观察:
句柄数量是否长期单调增长。
15.2 特征
例如:
| |
这种持续增长非常可疑。
16. 第 11 层:GDI / USER 对象泄漏
Windows GUI 程序还可能泄漏:
| |
症状:
- 窗口绘制异常
- 图标显示不正常
- 新窗口创建失败
- 程序运行时间越长越异常
任务管理器:
| |
加入相关列进行观察。
17. 第 12 层:程序自己“正常但太贪”
不是所有高内存都是 Bug。
例如:
数据库
可能主动把大量数据缓存到内存。
浏览器
标签页、扩展、页面缓存都可能占内存。
JVM
可能配置了较大的 Heap。
.NET
GC 不一定会把物理内存马上还给操作系统。
Python
内存释放后进程的 RSS 不一定立即明显下降。
因此:
看到进程内存下降不明显,不应该直接判定“泄漏”。
真正要看:
| |
18. 第 13 层:应用内存泄漏定位
如果确认:
| |
进入应用层排查。
18.1 第一优先级:能否稳定复现?
记录:
| |
例如:
| |
如果:
| |
比单纯“偶尔爆一次”更容易定位。
19. 第 14 层:不同语言的泄漏定位
C/C++
重点怀疑:
- malloc/new 后没有 free/delete
- 引用链
- 容器持续增长
- 缓存无上限
- 第三方库泄漏
工具:
| |
Java
重点:
| |
常见工具:
| |
典型思路:
| |
.NET
重点:
| |
工具:
| |
Python
重点:
| |
工具:
| |
20. “内存泄漏”最常见的 10 个根因
| 根因 | 特征 |
|---|---|
| 容器无限增长 | list/map/cache 越来越大 |
| 全局缓存不清理 | 用户/请求越多越大 |
| 线程泄漏 | 线程数不断增加 |
| 连接泄漏 | TCP/数据库连接不断增加 |
| 文件句柄泄漏 | handles/fd 持续增加 |
| 事件订阅未取消 | 对象一直无法回收 |
| 回调引用 | 对象被长期持有 |
| 定时器未释放 | 任务越积越多 |
| 大对象未释放 | 大数组/Buffer长期保留 |
| 第三方库/驱动 Bug | 应用代码看似正常 |
21. 第 15 层:线程泄漏
这是经常与内存泄漏一起发生的问题。
观察:
| |
如果三者一起持续增加:
非常值得怀疑资源泄漏。
Windows:
| |
22. 第 16 层:文件/连接资源泄漏
某程序不断创建:
| |
这些资源最后都可能间接造成内存压力。
排查方法:
| |
一起看。
23. “程序内存暴涨”的五种形态
形态 A:启动就高
| |
优先怀疑:
配置/正常数据规模。
形态 B:缓慢上涨
| |
优先:
泄漏。
形态 C:操作一次暴涨,然后不回落
| |
重点:
缓存 / 大对象保留 / GC行为。
形态 D:瞬间暴涨并崩溃
| |
重点:
峰值分配 / 大对象 / 批量处理。
形态 E:进程不高,但系统总内存不断涨
重点:
内核、驱动、缓存、压缩内存、虚拟化。
24. 第 17 层:Windows 事件日志 / 崩溃记录
出现 OOM、应用崩溃后:
打开:
| |
重点查:
| |
不要只看“错误”两个字。
要结合:
| |
25. 第 18 层:Resource-Exhaustion-Detector
如果系统出现:
“Windows 检测到内存不足……”
可以重点查看:
| |
查找:
| |
它可以帮助回答:
当时哪个进程占用了大量内存。
26. 第 19 层:Perfmon 性能监控
如果问题:
不是现在爆,而是过几小时/几天才爆。
不要一直盯任务管理器。
应该做:
长期采样。
Windows:
| |
建议记录:
| |
27. 最重要:做“内存增长曲线”
例如:
| 时间 | 总内存 | 进程 A | 进程 B | Commit | Handle |
|---|---|---|---|---|---|
| 10:00 | 48% | 1.2G | 0.8G | 35% | 3K |
| 10:30 | 55% | 2.1G | 0.8G | 42% | 5K |
| 11:00 | 63% | 3.1G | 0.9G | 50% | 8K |
| 11:30 | 72% | 4.3G | 0.9G | 60% | 13K |
| 12:00 | 85% | 6.1G | 1.0G | 77% | 21K |
很明显:
A 进程、Commit、Handle 一起上涨。
这比一句“内存爆了”有价值很多。
28. 修复原则:先临时恢复,再永久修复
28.1 临时恢复
可以:
| |
但必须认识到:
重启不是修复泄漏。
重启只相当于:
| |
然后问题可能再次出现。
29. 什么时候可以直接杀进程?
适合:
- 明确知道程序已经失控
- 工作已保存
- 这是可重启服务
- 当前目标是恢复系统可用性
不适合:
- 不知道是什么进程
- 数据库正在写入
- 文件正在保存
- 是系统关键进程
- 生产核心服务
30. 长期修复方法
应用泄漏
| |
驱动泄漏
| |
配置导致的高内存
例如:
| |
修复:
给资源设置合理上限。
31. 生产环境最重要的修复原则
不要只问:
“怎么让它不爆?”
应该问:
“为什么它会爆?”
以及:
“如果再次出现,系统能不能自动恢复?”
建议:
| |
32. 自动防护
对于服务程序,可以设计:
| |
注意:
自动重启是兜底,不应替代根因修复。
33. “整个系统卡死”的应急处理
优先级:
| |
如果是服务器:
不要直接重启生产服务器,先确认业务、集群和高可用状态。
34. Linux 内存问题快速排查
如果系统不是 Windows,而是 Linux:
看总体
| |
重点:
| |
最重要:
available
不要只看 free。
看进程
| |
持续观察
| |
看进程增长
| |
看 Swap
| |
看 OOM
| |
或:
| |
如果看到:
| |
说明:
内核 OOM Killer 已经杀进程。
35. Linux cgroup / Docker 容器内存 OOM
容器里非常常见:
| |
结果:
容器自己 OOM。
所以:
宿主机没爆 ≠ 容器没爆。
Docker
| |
检查:
| |
36. Java OOM 排查口诀
| |
特别注意:
Java 的
OutOfMemoryError不一定都是 Java Heap 爆满。
也可能:
- Metaspace
- Direct Buffer
- Native Memory
- Thread
- 文件句柄等
37. “OOM”不一定只有一种
看到:
| |
不要直接理解为:
“RAM 没了。”
可能实际上是:
| |
所以第一件事:
先看完整错误信息。
38. 常见故障案例
案例 1:电脑开机正常,下午越来越卡
现象:
| |
排查:
| |
结论:
某程序长期泄漏。
修复:
升级软件/修复程序/重启服务作为临时措施。
案例 2:任务管理器没有大进程,但内存一直高
排查:
| |
如果:
| |
重点:
驱动泄漏。
案例 3:内存 90%,但电脑并不卡
先看:
| |
可能:
大量内存只是缓存。
不要因为数字高就强行“清内存”。
案例 4:只有某服务运行 6 小时后越来越大
验证:
| |
这非常有价值。
说明:
问题高度集中在这个服务生命周期内。
案例 5:程序占用不算特别大,但系统提示内存不足
排查:
| |
可能:
提交内存接近上限,而不是单个 Working Set 特别大。
案例 6:处理一个大文件瞬间 OOM
例如:
| |
这更可能是:
峰值内存需求问题。
修复:
| |
案例 7:浏览器越来越吃内存
排查:
| |
使用:
| |
先找:
是哪个 Tab / 扩展。
案例 8:服务器运行几天后爆内存
优先做:
| |
不要等爆了才查。
案例 9:升级显卡驱动后开始内存异常
对比:
| |
如果时间高度相关:
回滚/更新驱动并验证。
案例 10:Docker 容器 OOM,但主机内存还有很多
检查:
| |
如果:
| |
说明:
是容器限制问题,不一定是主机 RAM 不够。
39. 一页判断口诀
| |
40. Windows 命令速查表
| 目的 | 命令 |
|---|---|
| 查看进程内存 | Get-Process |
| 内存排序 | Get-Process | Sort-Object WorkingSet64 -Descending |
| 查看线程 | Get-Process | Sort-Object Threads -Descending |
| 系统内存 | Get-CimInstance Win32_OperatingSystem |
| 查看进程详细信息 | 任务管理器“详细信息” |
| 性能监控 | perfmon |
| 事件查看器 | eventvwr |
| PoolMon | poolmon.exe |
| 查看系统信息 | msinfo32 |
41. Linux 命令速查表
| 目的 | 命令 |
|---|---|
| 总内存 | free -h |
| 进程内存排序 | ps aux --sort=-%mem |
| 持续观察 | watch -n 2 free -h |
| Swap | swapon --show |
| OOM | dmesg | grep -i oom |
| 内核日志 | journalctl -k |
| Docker | docker stats |
| 内存详细信息 | cat /proc/meminfo |
42. 不能乱做的操作
不要看到内存高就立刻:
| |
不要:
| |
不要:
| |
不要:
| |
不要:
| |
43. 排障时必须记录什么
最少记录:
| |
44. 内存泄漏排障记录模板
| |
45. 最终排障路线
以后遇到任何“内存爆了”,直接执行:
| |
46. 一句话总结
排查内存不要先问“怎么清内存”,要先问“谁在吃、吃的是什么、为什么一直吃、重启后为什么又回来”。
真正专业的内存排障,不是把“内存使用率”降下来,而是能够回答:
| |
这 7 个问题能回答清楚,内存问题基本就从“猜”进入了“证据驱动的定位”。