尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

虚拟内存配置与OOM排查:从页面文件到Docker容器实战

虚拟内存配置与OOM排查:从页面文件到Docker容器实战 很多教程告诉你虚拟内存就是“内存不够的时候拿硬盘顶上”这个定义严格来说没错但它太轻飘飘了以至于你根本不知道什么时候该配置、配置多少、配置错了会出什么问题。我见过 32GB 内存的机器因为页面文件设成 0导致编译项目时进程直接被系统秒杀也见过 8GB 的旧笔记本把页面文件放对盘之后稳稳跑着十几个应用。这篇文章不打算给你灌一堆“物理内存 vs 虚拟内存”的名词解释而是按我实际排查问题时的思路走一遍先搞懂原理再学会观察然后给配置参数最后用 Docker 里 Kafka 和 Elasticsearch 的 OOM 案例收尾。无论你只是偶尔遇到“内存不足”弹窗还是需要在 Windows 上长期跑开发环境都值得把这套思路完整过一遍。1. 先搞清楚虚拟内存里到底装的是什么1.1 物理内存不够时Windows 在“糊弄”谁把物理内存想象成办公桌桌面页面文件pagefile.sys就是旁边的抽屉。桌面上摆不下文件时你会把暂时不用的塞进抽屉要用的时候再抽出来。Windows 的做法类似当物理内存有压力时把一些进程暂时没在访问的内存页写进磁盘上的页面文件腾出物理内存给正在活跃运行的程序用。这个“塞进抽屉”的过程叫换出page out“抽出来”叫换入page in。但这里有个关键点经常被误解虚拟内存不是“内存不够时才用”。即使物理内存还有空闲Windows 也可能在整体提交压力变大的时候主动换出一些页因为操作系统要对所有进程的虚拟地址空间做出承诺。这个“承诺”是有上限的上限就是物理内存大小加上所有页面文件大小之和Windows 里称为“提交限制”Commit Limit。进程申请内存时系统先记账式地告诉它“这块地方划给你了”不会立刻真给只有进程实际访问这些地址时才触发缺页中断由内存管理器把对应内容从磁盘读进物理内存。所以真正决定系统会不会报“虚拟内存不足”的不是物理内存还剩多少而是“提交量”有没有撞到“提交限制”的顶。打个比方你请了很多客人来家里吃饭进程申请内存你不需要真的同时摆好所有椅子物理内存页你只需要保证家里加上临时租的折叠椅页面文件能坐下所有人就行。如果客人一来就全部找座位坐而且座位总数不够那场面就会失控。内存管理器的工作就是不断预判哪些客人会坐下、哪些只是在门口站着说话把有限的椅子调度给真正坐下来的客人。1.2 页面文件、工作集、提交限制三个必须知道的数要理解虚拟内存你只需要盯住三个概念其他杂七杂八的术语都可以往后放。页面文件pagefile.sys存放在某个或某几个分区的隐藏系统文件是虚拟内存的主要“磁盘后备”。Windows 10/11 还多一个 swapfile.sys主要用于 UWP 应用和小型内存管理一般不需要手动碰。工作集Working Set进程当前真正驻留在物理内存里的页面集合。任务管理器里那个“内存”列默认显示的就是工作集它反映的是这个进程此刻占用了多少物理内存。提交限制Commit Limit与提交量Committed Bytes提交限制约等于“物理内存 所有页面文件大小”。任务管理器“性能”页的“内存”标签下会显示“已提交 X/Y GB”X 是当前所有进程的提交量总和Y 就是提交限制。只要 X 长期逼近 Y比如 15.2/16.0 GB系统离报错就不远了。记住一句话任务管理器里的“可用内存”低不代表马上要崩但“已提交 X/Y”里的 X 接近 Y才是真正的红色警报。我有一次帮同事排查机器可用内存看着还有 3GB结果系统疯狂弹“内存不足”就是因为提交量已经顶到上限页面文件设得太小物理内存其实够用但系统的承诺额度已经用完了。2. “内存不足”和 OOM先别急着怪虚拟内存2.1 三种表现对应三种病因遇到报错不要直接归因到“虚拟内存没设置好”。Windows 环境下的“内存不足/OOM”至少分三类搞混了会白折腾很久。第一类是系统弹窗提示“你的系统虚拟内存不足”或“系统资源不足无法完成请求的服务”。这通常意味着提交量撞上了提交限制是真正的虚拟内存容量问题需要增大页面文件或者关掉一些吃提交量的大户。第二类是应用程序本身报 OutOfMemoryError最常见的是 Java 程序抛出java.lang.OutOfMemoryError: Java heap space。这是应用自己堆空间不够跟 Windows 页面文件大小关系不大。你看任务管理器时可能物理内存还剩一大半但 Java 进程的堆已经满了这时候需要调 JVM 参数而不是去改页面文件。第三类是系统蓝屏比如KERNEL_DATA_INPAGE_ERROR0x0000007A。这通常不是“容量不够”而是页面文件所在的磁盘读写失败、坏扇区或文件系统错误导致系统无法把数据换入换出。这时候盲目加大页面文件纯粹是火上浇油得先查硬盘健康度。2.2 任务管理器里真正值得看的几个数字很多人打开任务管理器只看“内存”百分比高一点就紧张低一点就放心。这太粗糙了。我平时判断内存压力只看几个数已提交 X/YX/Y 越接近 1风险越高。如果 X 经常到 Y 的 90% 以上就说明提交量快把额度吃完了。可用内存小于物理内存的 10% 要警惕但不一定立刻出问题还得配合页面文件读写的速率看。内存组合图任务管理器内存页底部如果“已提交/缓存”占了大半“可用”被压得很薄同时磁盘活动明显变高说明系统正在疯狂换页程序会表现得很卡。如果只在某一瞬间看到提交量飙升不用太慌。看长期趋势才有意义。可以用性能监视器perfmon添加计数器关键指标是\Memory\Committed Bytes和\Memory\Commit Limit记录几天的数据观察峰值离上限还差多少。这比任何网上流传的“16G 内存该设多少虚拟内存”都要准确因为它是你机器真实负载下的数据不是拍脑袋。2.3 dump 日志是甩锅利器也是定位铁证遇到反复出现的 OOM别靠猜抓证据。Windows 应用崩溃时事件查看器里会有 Application Error 事件里面能看到出错的模块和异常代码。这些信息不够细的时候就要抓 dump。抓用户态进程 dump 有几个办法任务管理器右键崩溃或异常高内存的进程 →“创建转储文件”会生成一个 .dmp 文件路径一般类似C:\Users\xxx\AppData\Local\CrashDumps。命令行用 procdumpprocdump -ma -e -x C:\dumps notepad.exe可以设置触发条件比任务管理器灵活很多。Java 服务提前加参数-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPathC:\dumps\OOM 时自动生成 hprof 文件再用 MATMemory Analyzer Tool打开分析堆里什么东西占满了。如果是蓝屏 dumpWindows 会写入C:\Windows\Minidump目录用 WinDbg 打开后执行!analyze -v能看到蓝屏的直接原因和涉及的驱动。我遇到过一台服务器频繁蓝屏朋友一直以为是内存条故障结果 dump 分析出来是某显卡驱动尝试做非法内存操作更新驱动后问题消失。没有 dump 的话这种问题排查起来就是大海捞针。3. 配置虚拟内存从自动托管到手动精确控制3.1 设置入口与页面文件的迁移路径Windows 10 和 11 的虚拟内存设置入口完全一样右键“此电脑” →“属性” →右侧“高级系统设置” →“性能”栏里的“设置” →“高级”选项卡 →“虚拟内存”栏里的“更改”。到这个窗口后第一件事是看“自动管理所有驱动器的分页文件大小”有没有勾选。如果勾着底下所有选项都是只读的必须先取消勾选才能手动配置。取消勾选后你会看到驱动器列表。选中 C 盘可以选“系统管理的大小”或“自定义大小”。很多教程会叫你直接填初始大小和最大值但有一个要命的细节填完数字之后必须点一下右边的“设置”按钮让配置真正写入当前驱动器那一行然后再点“确定”。如果你填完数字直接点“确定”Windows 很可能没有任何反应下次打开发现还是原来的配置。这个“设置”按钮是新手最容易漏的我帮不少人远程排查时发现他们改了几次都没生效就是这个原因。如果你想迁移页面文件到非系统盘正确做法是先在 C 盘选中“无分页文件”点“设置”再选中目标盘比如 D 盘或 E 盘设置系统管理的大小或自定义大小点“设置”最后全部点“确定”并重启。重启一次后C 盘根目录的 pagefile.sys 会消失新盘上会生成新的页面文件。3.2 16GB/32GB 内存的推荐配置与计算逻辑网上流传最广的说法是“初始大小设为物理内存的 1.5 倍最大值设为 3 倍”这套口诀放在今天已经很过时了。现代 Windows 的虚拟内存管理逻辑更复杂物理内存大了之后页面文件更多是承担“提交额度”和“系统崩溃转储”的角色而不是单纯给物理内存兜底。按我的经验先给一组快速可用的参考值再告诉你背后的计算逻辑。物理内存页面文件初始大小页面文件最大值典型场景8GB4096MB8192MB日常办公、网页多开16GB8192MB16384MB开发、虚拟机、Docker32GB16384MB32768MB大型编译、多容器、视频处理64GB 及以上物理内存的 25%物理内存的 50%按实际提交量动态调整这个表只是一个起点。更可靠的做法是监控“已提交 X/Y”里的 X。具体流程是把页面文件先设成“系统管理的大小”跑个三五天在任务管理器性能页里看“已提交”的峰值。假设物理内存 16GB页面文件系统托管峰值提交量是 12GB提交限制约 20GB16GB 物理 4GB 页面文件离顶还远得很那你完全可以保持系统托管什么都不用改。如果峰值已经到 18GB提交限制被顶到 20GB 左右那就需要把页面文件调大到 8GB 或 16GB把提交限制抬起来。注意一个反向误区物理内存很大的机器比如 32GB、64GB不代表页面文件可以一删了之。系统崩溃转储需要页面文件保留空间某些驱动和内核组件也要靠提交量工作。你把页面文件设为 0等于把系统在极端情况下的安全网撤掉了。3.3 SSD 环境下页面文件要不要固定大小关于 SSD 和虚拟内存的关系我听过太多错误说法什么“SSD 伤寿命不能开虚拟内存”“要把虚拟内存移到机械盘保护固态”之类全是扯淡。SSD 的随机读写性能比机械盘高一个数量级页面文件放 SSD 上能明显减少换页卡顿。现代 SSD 擦写寿命也没那么脆弱日常页面文件的写入量远小于视频渲染、下载缓存这些操作。为了“保护 SSD”而关掉页面文件等于为了省油把备胎拆了。那固定大小还是系统托管各有适用场景。系统托管的优点是省心页面文件会随压力自动扩展缺点是会发生动态扩展扩展瞬间磁盘 IO 会变高文件也可能碎片化。固定大小则把空间一次占好系统不需要在运行时动态扩容性能更稳定但前提是你得设够。如果固定大小设小了提交限制不够系统会直接报虚拟内存不足比动态扩展更麻烦。我的建议如果你不想折腾保持“系统管理的大小”就是最优解Windows 自带的自动管理已经很成熟如果你想手动固定大小就先把默认设置下跑了几天之后的提交峰值摸清楚再给页面文件一个“峰值提交量 - 物理内存 20% 余量”的估计值宁可大一点也别小。4. 常见配置错误与连环踩坑排查实录4.1 关闭页面文件后系统变得卡顿一次完整复盘前阵子有位读者发来求助说自己的电脑 32GB 内存看了某篇“内存够大就不需要虚拟内存”的文章后把页面文件设成了“无”。日常用没问题但一开 VMware 虚拟机再跑 Visual Studio系统就开始卡死任务管理器直接显示红条最后某程序直接被系统秒掉没有任何报错弹窗。这个案例很有代表性。排查路径大致是这样的打开任务管理器 →“性能”→“内存”看到“已提交”显示 XXX/16.0 GB注意这里的上限是 16GB 而不是 32GB。因为页面文件关闭后提交限制只剩物理内存的大小而且由于其他系统保留开销实际可用额度甚至比物理内存还小一点。再看事件查看器系统日志里能看到来源为Resource-Exhaustion-Detector的事件内容是系统提交计数达到上限。确认根因不是物理内存不够是提交额度不够。32GB 内存全部被进程占用时系统连换页缓冲的余地都没有某些启动时声请了大块虚拟地址空间的程序比如 JVM 保留堆、GPU 驱动缓存直接申请失败。修复方案很简单重新打开虚拟内存设置把页面文件恢复成“系统管理的大小”或手动设 16GB-32GB点“设置”后重启。重启后读者反馈虚拟机正常了编译也不再中途被杀。这台机器之后的长期维护里页面文件一直保持在 16GB 以上再没出现过类似问题。这个案例让我想多说一句那些推荐“大内存用户直接禁用虚拟内存”的教程基本都没讲明白提交限制的概念。你可以有 64GB 物理内存但如果把页面文件关了系统的提交限制就约等于 64GB而且这 64GB 还得包含所有驱动的非分页池和硬性保留。一些应用特别是 Java、Go 写的服务启动时申请虚拟地址空间是几 GB 甚至几十 GB 地“保留”实际访问的远没那么多。这种“保留”也要算进提交量没有页面文件就会很窘迫。4.2 蓝屏 KERNEL_DATA_INPAGE_ERROR 与页面文件损坏另一种让人一头雾水的配置错误是页面文件所在分区出了问题导致的蓝屏。典型代码是KERNEL_DATA_INPAGE_ERROR参数 0x0000007A有时后面还跟着NTFS_FILE_SYSTEM0x00000024。有段时间我手上一台工控机频繁蓝屏每次都在读写大文件时发生。一开始我也以为是内存条接触不良跑了两天 MemTest 完全没报错。后来用 CrystalDiskInfo 看硬盘 SMART 信息发现 05 项重映射扇区计数已经是黄色警告。再用chkdsk C: /f扫描确实扫出一堆坏扇区。这才确定是页面文件所在的机械盘坏道导致系统无法正常读换页。遇到这类问题排查顺序应该是先看蓝屏代码如果是 7A 这种“页数据读不到”的类型重点查磁盘而不是内存。重启进入恢复环境在命令行执行chkdsk C: /f /r它能标记坏扇区并修复文件系统错误。用 CrystalDiskInfo 或 HD Tune 这类工具看硬盘 SMART 健康度特别是 C5、C6 两个未决错误计数。如果硬盘有坏道立刻把页面文件迁移到健康的盘比如另一块 SSD再考虑更换硬盘。页面文件损坏还有一个隐蔽原因强行断电或系统异常崩溃时页面文件里的数据可能处于不一致状态。所以你在系统恢复后偶尔会看到“页面文件损坏已创建临时页面文件”的提示。这种情况通常重启几次会自动重建不必太担心。4.3 被“杀毒软件/优化工具”篡改后的恢复方案很多所谓“系统优化工具”“内存清理工具”会把虚拟内存设置当成优化项目默认给改成“无分页文件”或者一个很小的固定值。你看不到它干了什么但某天打开虚拟内存设置发现“自动管理”被取消了页面文件大小变成 256MB甚至整个列表都是“无”。恢复方法很简单打开虚拟内存设置窗口取消勾选“自动管理所有驱动器的分页文件大小”。选中系统盘通常是 C 盘选“系统管理的大小”点“设置”再点“确定”。如果 C 盘那一行无法修改先检查当前登录账户是否有管理员权限或者是否有安全软件把注册表保护起来了。如果图形界面设置被某些加固策略禁用了可以用注册表恢复。虚拟内存的相关键值在HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management下的PagingFiles默认值一般是C:\pagefile.sys 4096 8192第一位是路径后面两个数字是初始大小和最大值。用 regedit 修改后重启即可。也可以用 PowerShell 快速查看当前状态Get-CimInstance Win32_PageFileUsage | Select-Object Name, AllocatedBaseSize, CurrentUsage, PeakUsage这句话能看到每个页面文件的实际分配大小、当前使用量和峰值使用量。如果CurrentUsage长期接近AllocatedBaseSize就说明大小不够该扩容了。5. 实际 OOM 场景复盘Docker、Kafka、Elasticsearch 在 Windows 上到底该调谁5.1 JVM 的 OOM 与系统内存不足不是一回事Java 系的应用在 Windows 上跑得多出问题也多这里有个最容易混淆的点java.lang.OutOfMemoryError不等于 Windows 内存不足。JVM 自己管理一块堆内存堆大小由-Xmx参数决定。堆满了抛Java heap space这是 JVM 内部的容量问题操作系统页面文件再大也帮不上忙因为你得调大-Xmx或者修代码里的内存泄漏。反过来如果 JVM 启动参数把-Xmx设得很大比如宿主机 16GB 给容器分配了 12GB然后容器里还跑着页缓存和其他进程那 Windows 物理内存就会被挤爆最终触发的是宿主机层面的内存压力而不是 JVM 的堆溢出。报错形式可能是容器被 OOM killedDocker 日志里出现memory cgroup out of memory或者 WSL2 直接崩溃。所以我每次遇到“Java 应用 OOM”都会先看两样东西一是应用日志里有没有java.lang.OutOfMemoryError以及具体是哪一种二是任务管理器里的“已提交 X/Y”有没有接近上限。前者指向 JVM 内部后者指向系统层面。方向判断错了改-Xmx还是改页面文件就会南辕北辙。5.2 Docker Desktop 占满内存的解法Windows 上最常见的系统性 OOM 其实是 Docker Desktop 造成的。Docker Desktop 如果基于 WSL2 后端点默认内存管理方式很“大气”在部分机器上会把宿主机内存的 50% 甚至更多都划给 WSL2。你本机 16GB 内存WSL2 可能直接吞掉 10GB剩下的 Windows 桌面和开发工具再挤一挤提交量很容易就顶到计算机的提交上限。这种场景下的解法不是盲目的去宿主机内存设置页面加大页面文件而是先限制 Docker Desktop 和 WSL2 的资源占用。在用户目录下创建或编辑.wslconfig文件[wsl2] memory8GB processors4 swap2GB保存后执行wsl --shutdown然后重启 Docker Desktop。这里swap2GB是给 WSL2 内部用的交换文件它运行在宿主机磁盘上和 Windows 的页面文件是两码事不要混在一起。限制完 WSL2 之后再回头检查 Windows 自身的内存提交压力。如果此时“已提交 X/Y”里 X 还是很接近 Y说明宿主机的页面文件也需要放大。我们单位有几台跑 Docker 的开发机16GB 物理内存页面文件常年保持 16GB 左右配合.wslconfig里 6-8GB 的 WSL2 限制运行 Kafka、MySQL、Redis 全家桶都很稳。5.3 一个 Kafka 容器 OOM 的排查实例最后分享一个我最近处理的案例里面几乎包含了前面说到的所有知识点。一台 Windows 11 笔记本16GB 内存装了 Docker Desktop里面跑了 Kafka 和 MySQL 容器。现象是跑个半天系统可用内存掉到几百 MB磁盘 100%容器日志出现 OOM killedWindows 还会弹出“虚拟内存不足”。排查时我先看任务管理器发现“已提交”是 15.6/16.0 GB几乎撞到顶。这说明系统层面的提交额度已经用完物理内存也压得难受。再看 Docker Desktop 设置发现 WSL2 没有配置内存上限默认吃掉了大部分可用内存而 Kafka 容器的 JVM 堆是默认值日志里频繁出现垃圾回收耗时过长明显是堆偏小造成的。整个处理过程分四步在.wslconfig里限制 WSL2 内存为 6GB、处理器 4 核执行wsl --shutdown后重启 Docker Desktop。给 Kafka 容器设置合理的 JVM 堆大小通过环境变量KAFKA_HEAP_OPTS-Xmx2g -Xms2g。别以为堆设得越大越好Kafka 本身还要依赖操作系统的页缓存做高性能读写你堆设得太大页缓存就没空间了反而更差。给宿主机页面文件扩容从“系统托管”改为自定义 12GB-24GB。这一步是给整体提交限制兜底避免某个瞬时高峰触发系统级 OOM。用性能监视器观察了一周确认“已提交”峰值在 12GB 左右没有逼近新的提交限制才算真正收工。这个案例说明一件事当应用和容器把物理内存吃满时正确顺序是先给它们设上限再调整宿主机页面文件做兜底。反过来先无脑调大页面文件只会让磁盘 IO 变得更高卡顿更明显。虚拟内存可以解决“提交额度不够”的问题但解决不了“某个应用真的需要更多堆内存”的问题。这两者必须分清。
返回列表