资讯动态

Windows任务管理器内存列:工作集、专用工作集与提交大小拆解

发布时间:2026/10/2 5:14:11 来源:尧图企业网站定制
任务管理器里那几列内存数字我见过太多人看错。有人盯着内存列发现某个进程吃了8GB转头就断言这程序内存泄漏也有人看到提交大小比物理内存还大以为机器已经爆了还有人把一排进程的内存列加起来发现比总物理内存多出好几个GB怀疑任务管理器在骗人。工作集、内存专用工作集、提交大小这三个名字都带内存可它们分属三本完全不同的账。搞不清边界做容量规划会翻车排查泄漏会找错方向连该不该加内存条都判断不了。这篇就把Windows任务管理器里这几个内存列拆开讲透从虚拟内存的基本模型讲到每一列对应的内核计数器再给几段能直接跑的验证代码最后聊聊我在实际排查里踩过的那些坑。内容偏实战运维、后端、桌面应用开发、以及单纯想搞懂自己电脑为何卡的读者都能用上。1. 先把内存的账本理清楚为什么任务管理器要分这么多列1.1 从一张内存地图说起虚拟地址、提交、物理页先建立一个心智模型。每个Windows进程拿到的都不是真实的物理内存条而是一整套私有的虚拟地址空间。在64位系统上这个空间大得离谱Windows 8.1之后单进程用户态能用到的虚拟地址是128TB级别。进程自己看着像拥有这么多内存实际上绝大部分地址是画饼从来没有被兑现过也永远不会被兑现。真正开始花钱的第一步叫提交Commit在进程维度上叫私有提交、Private Commit。当进程申请内存并选择提交模式或者堆管理器为对象分配空间时系统就在内核里记一笔账这个进程欠了多少字节的内存。这笔账汇总到系统的提交限制Commit Limit总账本上。你可以粗暴理解成提交限制 ≈ 物理内存容量 当前页面文件实际大小任务管理器性能标签页里那个已提交 x/yy就是提交限制。注意它是动态的页面文件按需增长时y会跟着变不是你当初在系统设置里填的那个最大值。这一点后面参数规划部分还会展开。第二步叫触碰Touch。提交只是记账物理内存条上一页都还没给。只有当进程真的往某个地址写数据或者读数据时CPU触发缺页中断内存管理器才从可用内存池里取一页真实物理页框建立虚拟地址到物理页的映射然后把这个页框算进进程的工作集。所以顺序非常清楚提交 → 触碰 → 驻留。三个动作对应三个数字任务管理器里那些看起来类似的列名全都是这条链上不同位置的快照。1.2 三个数字的层级关系到底是什么先给结论再补细节提交大小 ≥ 工作集 ≥ 专用工作集提交大小是这个进程向系统认领过的内存总量包含已经驻留的、被换出去的、以及压根没碰过的地址。它是内存的应付款。工作集是这个进程此刻挂在物理内存上的页数总和包含自己的和跟别人共享的。它是实际占地。专用工作集是工作集里只属于这个进程、别的进程碰不到的那部分共享的DLL代码页、内存映射文件页都被剔除。它是独占占地。最容易混的一点提交大小在工作集被换出之后不会自动下降。进程主动调用释放接口把内存还回去提交才降。所以提交大小是个长期趋势线适合看泄漏工作集是随系统压力上下浮动的浮标适合看当下压力。还有一个隐藏角色叫虚拟大小Virtual Size。它统计的是进程保留的虚拟地址空间总量包含大量永远不提交的保留区数值往往比提交大小大好几倍。在网上看到有人贴出几十GB的虚拟大小然后惊呼中毒基本可以判定是没分清保留和提交。2. 逐列拆解工作集、内存专用工作集、提交大小到底在说什么2.1 内存列显示的到底是工作集还是专用工作集这个坑我必须放在最前面。Windows 10/11的任务管理器里有两个地方能看内存进程标签页的内存列把鼠标停在列头上会看到提示内存(活动的专用工作集)这里的数字是专用工作集。详细信息标签页在列头上右键选择选择列可以一次性把工作集、提交大小、专用工作集、峰值工作集全勾出来这才是原始数据的全景。为什么进程页要用专用工作集而不用总工作集因为进程页是要给人快速扫一眼谁在占内存的。如果用总工作集20个Chrome渲染进程会各自把共享的公共DLL算一遍加起来虚高一大截用户看了只会更懵。用专用工作集数字收敛横向可比性好。顺带解释活动的三个字。Windows会把长时间没被访问的工作集页移到待命列表Standby List这些页还在内存里但已经不算进程的活跃工作集。加活动的限定是为了强调只统计当前真正挂在工作集里的部分。2.2 内存专用工作集怎么算出独占这两个字专用工作集的算法就是工作集减去可共享页。哪些页算可共享主要是三类映像文件的代码页也就是exe、dll被加载后映射进来的部分。同一份kernel32.dll被200个进程映射物理内存里只有一份200个进程的工作集里却各算一份。内存映射文件页。用文件映射方式读写大文件时页是从磁盘文件映射进来的天然可共享。页文件支持的共享段。某些进程间通信会用共享内存段这些段也能被多个进程看到。专用工作集的价值在这里把一堆进程的专用工作集加起来得到的是接近真实的私有物理占用。而把工作集加起来结果会明显超过物理内存总量这不是系统算错了是共享页被重复计数。注意专用工作集低不代表进程不占内存。一个进程如果主要通过内存映射文件读一个10GB的数据文件它的专用工作集可能只有几十MB但工作集会很大Shared可共享列也一样大。看到这种情况别急着下这进程很轻的结论。2.3 提交大小与私有提交内存泄漏最先在这里露头提交大小对应性能监视器里的\Process(进程名)\Private Bytes注意是Private Bytes不是Virtual Bytes。用一个表格把三个容易混的计数器摆在一起任务管理器列性能计数器含义典型用途提交大小\Process(v)\Private Bytes已提交、未共享的字节数判断泄漏、容量规划工作集\Process(v)\Working Set物理驻留总页数判断当下内存压力专用工作集\Process(v)\Working Set - Private物理驻留中的独占部分横向比较进程占用虚拟大小\Process(v)\Virtual Bytes保留的地址空间总量排查地址空间耗尽32位进程泄漏排查为什么盯提交大小因为它的单调性最好。一个健康的服务进程负载稳定时提交大小会在一个区间里小幅波动曲线像心电图。有泄漏的进程提交大小会呈现缓慢但坚定的台阶式上升跑三天涨500MB跑两周涨3GB重启后归零再涨——这个形状基本可以直接定性。工作集就不行系统内存紧张时它会被页换出、被裁剪曲线会掉下来反而掩盖了泄漏。这就是为什么很多人看内存列以为进程很乖实际上提交已经涨上天了。2.4 顺手把分页缓冲池、非分页缓冲池、内存压缩讲明白性能标签页里还有几个数值得认识不然排查时容易误判分页缓冲池Paged Pool内核对象用的内存可以被换出到页面文件。驱动泄漏最常见的地方。非分页缓冲池Non-paged Pool内核对象用的内存任何时候都必须在物理内存里不能被换出。这个数异常增长正常机器通常几百MB以内通常是驱动问题的强信号。内存压缩CompressedWindows 10之后引入内存吃紧时把不活跃的页压缩后留在物理内存里而不是写盘。所以内存压缩进程本身占用不低是正常的它的存在反而减少了磁盘I/O。硬件保留Hardware Reserved固件和设备预留走的物理内存任务管理器里显示为不可用。集显机器上这个数常见几百MB到1GB属于正常现象不是被什么软件偷了。3. 亲手验证用小实验把三个数字的差异跑出来3.1 工具清单与打开方式光看文档记不住动手做一遍才知道每个数字长什么样。准备下面这几样都是系统自带或者免费的工具看什么打开方式任务管理器概览、提交总量CtrlShiftEsc资源监视器工作集/可共享/专用三列并排开始菜单搜资源监视器或运行resmon性能监视器长时间曲线、自定义计数器运行perfmonPowerShell批量导出进程内存数据系统自带Process Explorer比任务管理器更细的进程视图Sysinternals套件VMMap单进程内存类型细分私有/映像/映射/堆Sysinternals套件资源监视器的内存页是我最推荐的日常工具因为它把工作集、可共享、专用三列直接并排放着关系一目了然。任务管理器为了界面简洁反而藏了信息。3.2 三段式实验分配、触碰、释放看每一列怎么走下面这段Python代码能直观演示提交先动、工作集后动的过程。用ctypes直接调Windows API只提交不触碰再手动触碰观察两个数字的时间差。前提是你装了一个64位的Python。import ctypes import time kernel32 ctypes.windll.kernel32 # 这四行是关键不设置的话64位指针会被截断成32位直接崩溃 kernel32.VirtualAlloc.restype ctypes.c_void_p kernel32.VirtualAlloc.argtypes [ ctypes.c_void_p, ctypes.c_size_t, ctypes.c_ulong, ctypes.c_ulong ] kernel32.VirtualFree.argtypes [ ctypes.c_void_p, ctypes.c_size_t, ctypes.c_ulong ] MEM_COMMIT 0x1000 MEM_RESERVE 0x2000 MEM_RELEASE 0x8000 PAGE_READWRITE 0x04 SIZE 400 * 1024 * 1024 # 400MB print(阶段一提交400MB但一页都不碰) ptr kernel32.VirtualAlloc(None, SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE) print(地址:, hex(ptr)) time.sleep(30) # 这30秒去看任务管理器提交大小涨了工作集几乎没动 print(阶段二逐页触碰触发缺页物理页才真正分配) ctypes.memset(ptr, 0x41, SIZE) time.sleep(60) # 这时工作集和专用工作集才会同步涨到400MB附近 print(阶段三释放) kernel32.VirtualFree(ptr, 0, MEM_RELEASE) time.sleep(30) # 提交和工作集会一起掉下去跑的时候把任务管理器切到详细信息标签页先给进程名那一列右键加上工作集、专用工作集、提交大小三列然后盯着python.exe这一行看。你会发现阶段一提交大小直接跳400MB工作集几乎不变阶段二工作集才爬上去阶段三一起掉回来。这个时间差就是虚拟内存和物理内存最直观的分界线。再补一个映射文件的实验展示工作集涨、专用工作集不怎么涨的现象。准备一个大文件比如几GB的日志然后import mmap import os import time path rD:\bigfile.bin f open(path, rb) m mmap.mmap(f.fileno(), 0, accessmmap.ACCESS_READ) print(逐页触碰映射区域前50MB注意别看read()read会复制出私有内存) for off in range(0, 50 * 1024 * 1024, 4096): _ m[off] # 单字节访问只把映射页拉进工作集 time.sleep(60) # 这时候看资源监视器工作集涨、可共享涨、专用基本不动 m.close() f.close()这个实验解释了为什么有些数据密集型程序的专用工作集看起来很秀气——它的内存主要是文件映射页属于可共享部分被排除在专用之外了。3.3 用性能监视器建立长时间基线单次采样说明不了问题抓泄漏要看趋势。用perfmon加下面这组计数器采样间隔设为15秒跑一整天\Process(你的进程名)\Private Bytes—— 看泄漏主力\Process(你的进程名)\Working Set—— 看物理占用\Process(你的进程名)\Working Set - Private—— 看独占部分\Memory\Committed Bytes和\Memory\Commit Limit—— 看整机水位\Memory\Available MBytes—— 剩余可用物理内存\Memory\Pages/sec—— 换页活跃度持续偏高说明内存真的不够\Memory\Page Reads/sec—— 硬错误即真的要从磁盘读页这里有个经验判断Available MBytes长期低于总内存的10%同时Pages/sec持续大于几十两者一起出现才叫内存紧张。只看Available低就慌很多时候那只是系统把缓存用满了属于健康的用法。4. 生产环境常见场景与排查思路4.1 提交大小一路涨工作集却很平稳这是我遇到最多的一类问题。某服务跑了一周提交从800MB涨到4GB工作集一直在1.2GB附近晃。结论基本可以定成提交泄漏常见原因有四种第一缓存对象只加不减。写代码时用字典或者列表做本地缓存加了过期时间却没写清理逻辑或者清理逻辑依赖的触发条件永远不满足。第二句柄或者GDI对象泄漏带来的间接提交增长。第三线程栈泄漏每创建一个线程默认提交1MB可调线程没回收提交就一路涨。第四内存分配器只向系统要内存、不还内存比如某些版本的分配器在多线程场景下会产生大量碎片化的堆段提交难看但工作集还能撑住。排查顺序我一般是这样先用VMMap挂上去看堆和私有数据的细分。如果是托管代码直接把进程dump下来看托管堆如果是原生代码看私有数据和堆段的数量增长。判断方法很朴素——正常进程的堆段数量应该稳定如果堆段数量随时间线性增长就是分配器的锅优先考虑换分配器或者调整参数而不是继续加内存。4.2 专用工作集很高但系统还没有明显换页这种情况经常被误判成内存泄漏其实很多是设计使然。比如数据库会主动把缓存做大Java服务会按堆参数把堆占满这些程序的工作集高是预期行为。判断办法是看它的曲线形状一次性涨上去然后走平的是缓存阶梯式持续上台阶的才怀疑泄漏。另一个判断维度是看它是否可回收。任务管理器里可以对某些进程点结束任务看它释放多少但更文明的办法是查该服务有没有配置上的缓存上限。我做过一个统计口径的调整把服务的堆上限从无限制改成物理内存的60%配合监控里加一条提交大小超过阈值就告警之后同一台机器上跑了三个月都没再出过问题。核心不是内存不够而是没人给它划边界。4.3 所有进程加起来超过物理内存是不是任务管理器坏了不是。这是共享页重复计数的必然结果。举个数一台16GB内存的机器200个进程各自映射同一份公共运行库如果每个进程的工作集里这份库占20MB加起来就是4GB但物理内存里只有一份20MB。差出来的那部分纯属重复计数。所以判断整机内存水位永远不要自己加列。看两个权威数字就行性能页的已提交和可用。已提交对提交限制可用对物理内存。这两个数才是系统层面的真相。还有一点值得提醒内存压缩功能开启时一部分本该换出到页面文件的页被压缩后留在内存里。所以你会看到已提交很高但硬盘灯不亮这是内存压缩在起作用属于优化而不是故障。提示如果发现非分页缓冲池持续增长到几GB不要犹豫优先排查第三方驱动和杀毒软件的过滤驱动。这个值正常情况下应该很平稳。4.4 常见问题速查表现象大概率原因处理方向提交大小涨、工作集平提交泄漏、缓存无上限dump分析、加缓存上限、设告警工作集高、专用低大量文件映射正常现象确认映射量符合预期进程内存加总超物理内存共享页重复计数看系统已提交和可用别手动加非分页缓冲池持续增长驱动泄漏排查驱动、更新固件与驱动硬错误/秒持续偏高真实内存不足扩容或降低并发硬件保留占几百MB固件与集显预留正常不用管某进程工作集显示为0进程已挂起或已进入待命结合状态列一起看5. 把数字用起来容量规划与调优的几个经验5.1 内存红线到底怎么算才靠谱我给业务机器划红线时不看工作集看提交大小的峰值。方法是压测跑一轮完整的业务高峰用perfmon记录整机\Memory\Committed Bytes的峰值然后按下面的口径留余量推荐内存 提交峰值 × 1.3预留增长与突发为什么用提交峰值而不是平均因为提交是不会被系统自动回收的它反映的是最坏情况下的真实需求。为什么系数取1.3而不是2留太多会造成大量内存闲置成本上不划算留太少一次突发流量就会触发换页性能直接掉崖。1.3这个系数是我在多个中等并发服务上反复验证过的经验值具体可以按业务突发的剧烈程度微调。还有一条底线如果监控显示硬错误持续大于每秒几十次不管提交水位多少都说明物理内存跟不上当前工作负载了扩容的收益比任何代码优化都直接。5.2 页面文件该不该关该设多大网上关了页面文件能省磁盘、提升性能的说法害人。页面文件承担两件事一是给提交限制提供额度二是承接不活跃页。关掉它提交限制就等于物理内存只要有一个进程申请内存超过剩余额度分配直接失败程序报内存不足然后崩掉哪怕物理内存还剩一大半。我的做法是这样SSD机器保留系统管理的页面文件不手动设固定值。服务器上如果为了可预测性要固定大小按物理内存的1倍作为初始值峰值提交的1.5倍作为最大值来设。跑数据库这类会自己管理缓存的程序时把它自己的内存上限压到物理内存的70%左右给系统和其他进程留空间这个比例比设置页面文件大小更影响稳定性。5.3 我踩过的几个坑第一个坑早年看Chrome的内存列加起来超过物理内存花了半天研究是不是被挖矿了最后发现是共享页重复计数。这件事让我养成一个习惯判断内存问题先看系统级的已提交和可用再往下看进程。第二个坑把某个进程的提交大小当成它的真实物理占用去规划容量结果规划出来的机型配置严重偏高。提交是申请额不是实际占用两者差着共享页、未触碰页和已换出页。第三个坑用任务管理器默认的进程页内存列去做历史对比换了系统版本之后发现数值口径变了导致趋势图出现断层。后来我改成用perfmon采集Working Set - Private和Private Bytes两个计数器入库口径固定跨版本可比性稳定。第四个坑给32位老程序做优化时只盯着提交大小忽略虚拟地址空间的碎片化。32位进程用户态只有2GB开了大地址感知是3GB或4GB虚拟空间长期运行后保留区碎片化明明提交才几百MB申请却失败。这类程序得看虚拟大小和保留区分布VMMap在这件事上非常好用。如果你平时用PowerShell批量盘点把下面这段存成脚本每天跑一次写进日志慢慢就能积累出每个进程的正常波动区间出问题的时候一眼就能看出哪个不对劲Get-Process | Sort-Object WorkingSet64 -Descending | Select-Object -First 20 Name, Id, {nWS_MB;e{[math]::Round($_.WorkingSet64/1MB,1)}}, {nPrivateBytes_MB;e{[math]::Round($_.PrivateMemorySize64/1MB,1)}}, {nVirtual_MB;e{[math]::Round($_.VirtualMemorySize64/1MB,1)}}注意这里的PrivateMemorySize64拿到的是私有提交跟任务管理器的专用工作集不是一回事要专用工作集得走性能计数器\Process(*)\Working Set - Private。这点区别我在实际排查里反复确认过写脚本的时候千万别把两个名字记混了不然趋势图能骗你半年。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑