业务反馈“系统很卡”,你远程连上去打开任务管理器,看到 CPU 38%、内存用了 70%,然后呢?这两个数字本身几乎不能得出任何结论——Windows 的内存管理策略是“闲着也是闲着,不如拿来做缓存”,70% 占用可能完全健康;而 CPU 38% 若伴随处理器队列堆积,恰恰说明已经饱和。
要真正回答“CPU 和内存够不够用”,必须看对计数器、看懂阈值。本文从任务管理器和资源监视器的快速判读讲起,进入性能监视器(Performance Monitor,perfmon)的数据收集器集配置,再给出 typeperf 与 Get-Counter 的命令行长期采集方案,以及一套可直接落地的告警阈值表。
一、第一层:任务管理器与资源监视器
最快的入口是 taskmgr.exe(Ctrl+Shift+Esc)和 resmon.exe。直接运行 perfmon /res 可一步打开资源监视器,它比任务管理器信息密度高得多。
REM 打开资源监视器(推荐入口) perfmon /res REM 打开性能监视器 MMC perfmon.msc REM 任务管理器 taskmgr.exe REM 资源监视器独立入口 resmon.exe
资源监视器的五个选项卡里,与 CPU/内存相关的关键视图:
- 概述:CPU、磁盘、网络、内存四条总览曲线,一眼看出瓶颈在哪个子系统。
- CPU:按进程列出 CPU 占用、线程数、平均 CPU;展开某进程可看具体哪些线程在跑。
- 内存:这里是重点,列出了“提交(KB)”“工作集(KB)”“可共享(KB)”“专用(KB)”——看内存占用应该看“专用”或“提交”,而不是“工作集”。
- 磁盘 / 网络:排除 IO 瓶颈,很多时候“CPU 高”其实是等 IO 造成的假象。
内存术语必须先厘清,否则读数全是错的:
| 指标 | 含义 | 判断价值 |
|---|---|---|
| 工作集 Working Set | 进程当前驻留在物理内存中的页,含共享页 | 会被共享库放大,不适合作为独占占用依据 |
| 专用工作集 Private Working Set | 工作集中不可共享的部分 | 判断“这个进程自己吃了多少内存”最准确 |
| 提交大小 Commit Size | 进程向系统申请的虚拟内存承诺量 | 含已换出到页面文件的部分,反映真实需求量 |
| 可用内存 Available | 可被立即分配的内存(含备用/空闲) | 核心告警指标,比“已用百分比”有意义 |
| 已修改 / 备用 | 缓存与待写回页,可被回收 | 属于可用资源,不应计为“被占用” |
二、第二层:性能监视器与关键计数器
打开 perfmon.msc,左侧“性能监视器”默认只有一个 % Processor Time。点击绿色加号添加计数器,或按 Ctrl+N 切换为报表/直方图。真正需要长期盯的是下面这些计数器,按子系统分组。
# 列出本机所有可用的内存相关计数器(英文系统用英文名) Get-Counter -ListSet Memory | Select-Object -ExpandProperty Paths # 列出处理器相关计数器 Get-Counter -ListSet Processor | Select-Object -ExpandProperty Paths # 列出进程相关计数器 Get-Counter -ListSet Process | Select-Object -ExpandProperty Paths # CMD 等价:列出全部计数器名(可用 findstr 过滤) typeperf -q > C:\counters.txt typeperf -qx "\Memory"
2.1 核心计数器与阈值
| 计数器 | 健康参考 | 告警阈值与解读 |
|---|---|---|
| \Processor(_Total)\% Processor Time | 日常 < 60% | 持续 > 80% 需关注;> 95% 持续 5 分钟即需扩容或优化 |
| \Processor(_Total)\% Privileged Time | < 20% | 长期 > 30% 说明内核态开销大,多为驱动/中断/上下文切换 |
| \System\Processor Queue Length | < CPU 逻辑核心数 | 持续大于核心数 = CPU 已饱和,这是比百分比更可靠的饱和指标 |
| \System\Context Switches/sec | 视负载,通常数千 | 突然飙升至上万,多为锁竞争或线程数失控 |
| \Memory\Available MBytes | > 总内存 20% | < 总内存 10% 或 < 500 MB 时告警 |
| \Memory\Pages Input/sec | 接近 0 | 持续 > 0 表示发生硬页面错误,真实内存不足 |
| \Memory\Pages/sec | < 100 | 持续 > 1000 且伴随 Available 低 = 严重内存压力 |
| \Memory\% Committed Bytes In Use | < 70% | > 85% 说明提交内存逼近上限,需扩页面文件或物理内存 |
| \Memory\Pool Nonpaged Bytes | 稳定不增长 | 单调增长不回落 = 典型内核内存泄漏(常见于驱动) |
| \Process(_Total)\Handle Count | 稳态波动 | 持续增长不回落 = 句柄泄漏 |
| \Paging File(_Total)\% Usage | < 30% | > 70% 说明物理内存不足,开始依赖交换 |
判断顺序建议:先看 Processor Queue Length 判断 CPU 是否饱和,再看 Available MBytes + Pages Input/sec 判断内存是否真不够。这两个组合起来,就能避免“内存占用 90% 但其实很健康”的误判。
三、第三层:命令行采集(typeperf 与 Get-Counter)
图形界面适合临时观察,长期采集和自动化必须用命令行。
3.1 typeperf(CMD,兼容性最好)
REM 语法:typeperf <计数器> [-si 间隔秒] [-sc 采样次数] [-o 输出文件] [-f 格式] REM 采样 12 次,每 5 秒一次,输出到 CSV typeperf "\Processor(_Total)\% Processor Time" "\Memory\Available MBytes" -si 5 -sc 12 -o C:\perf\sample.csv -f CSV REM 持续采集直到 Ctrl+C(不加 -sc) typeperf "\Processor(_Total)\% Processor Time" "\Memory\Pages/sec" -si 10 -o C:\perf\long.csv -f CSV REM 多实例进程(如 IIS 的 w3wp 有多个实例)用 * 通配 typeperf "\Process(w3wp*)\% Processor Time" "\Process(w3wp*)\Private Bytes" -si 5 -sc 20
3.2 Get-Counter(PowerShell,灵活可管道)
# 单次采样
Get-Counter '\Processor(_Total)\% Processor Time', '\Memory\Available MBytes'
# 连续采样(每 2 秒一次),Ctrl+C 停止
Get-Counter '\Processor(_Total)\% Processor Time' -Continuous -SampleInterval 2
# 取样 10 次并计算平均值,这是给领导汇报的正确姿势
$cpu = (Get-Counter '\Processor(_Total)\% Processor Time' -SampleInterval 2 -MaxSamples 10 |
Select-Object -ExpandProperty CounterSamples | Measure-Object -Property CookedValue -Average)
"CPU 平均值: {0:N2}%" -f $cpu.Average
# 找出 CPU 占用最高的 10 个进程
Get-Counter '\Process(*)\% Processor Time' -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty CounterSamples |
Where-Object { $_.InstanceName -notin '_total','idle' } |
Sort-Object CookedValue -Descending |
Select-Object -First 10 InstanceName, @{n='CPU%';e={[math]::Round($_.CookedValue,2)}}
# 内存总览(WMI 方式,适合写监控脚本)
Get-CimInstance Win32_OperatingSystem | Select-Object `
@{n='TotalGB';e={[math]::Round($_.TotalVisibleMemorySize/1MB,2)}},
@{n='FreeGB';e={[math]::Round($_.FreePhysicalMemory/1MB,2)}},
@{n='FreePct';e={[math]::Round($_.FreePhysicalMemory/$_.TotalVisibleMemorySize*100,1)}},
@{n='VirtualTotalGB';e={[math]::Round($_.TotalVirtualMemorySize/1MB,2)}},
@{n='VirtualFreeGB';e={[math]::Round($_.FreeVirtualMemory/1MB,2)}}
# 内存占用 Top 10 进程(按专用工作集)
Get-Process | Sort-Object -Property PrivateMemorySize64 -Descending |
Select-Object -First 10 Name, Id,
@{n='PrivateMB';e={[math]::Round($_.PrivateMemorySize64/1MB,1)}},
@{n='WorkSetMB';e={[math]::Round($_.WorkingSet64/1MB,1)}} |
Format-Table -AutoSize
# 一次性输出性能快照对象
Get-CimInstance Win32_PerfFormattedData_PerfOS_Memory |
Select-Object AvailableMBytes, CommittedBytes, CommitLimit, PagesPerSec, PoolNonpagedBytes |
Format-List
四、第四层:数据收集器集与长期留存
要分析“昨天下午三点为什么卡”,必须有历史数据。Windows 用数据收集器集(Data Collector Set)做这件事。图形路径:perfmon.msc → 数据收集器集 → 用户定义 → 右键新建 → 数据收集器集 → 手动创建 → 性能计数器。
命令行用 logman,可脚本化、可批量下发:
REM 创建一个循环二进制格式的性能日志(-f bincirc 循环覆盖,-max 单位 MB) logman create counter PerfBase -o "C:\PerfLogs\PerfBase" -f bincirc -max 500 -si 30 -v mmddhhmm -c "\Processor(_Total)\% Processor Time" "\Memory\Available MBytes" "\Memory\Pages/sec" "\Memory\Pool Nonpaged Bytes" "\System\Processor Queue Length" "\Process(_Total)\Handle Count" "\LogicalDisk(_Total)\% Free Space" REM 启动 / 停止 / 查询 logman start PerfBase logman query PerfBase logman stop PerfBase REM 设置计划启动(每天 00:00 开始,采集 24 小时) schtasks /create /tn "PerfBase-Daily" /tr "logman start PerfBase" /sc daily /st 00:00 /ru SYSTEM /rl HIGHEST /f REM 把 .blg 二进制日志转成 CSV 便于分析 relog "C:\PerfLogs\PerfBase_*.blg" -f CSV -o "C:\PerfLogs\PerfBase.csv"
配置建议:采样间隔 30 秒是性能与精度的平衡点(关键业务可 15 秒,不建议低于 5 秒,采集本身也有开销);-f bincirc 循环模式可避免日志把磁盘写满;日志目录务必放在非系统盘。
五、典型问题的判读套路
- CPU 高,但 % Processor Time 里 Privileged 占比大:不是应用算得多,而是内核态开销——查
\System\Context Switches/sec、中断、驱动(DPC 时间)。 - Processor Queue Length 长期大于核心数:CPU 真饱和。要么优化热点代码,要么扩容;先看 Top 进程确认是不是单个进程吃满。
- 内存占用高但 Pages Input/sec ≈ 0、Available 充足:正常。Windows 用空闲内存做文件缓存(
\Memory\Cache Bytes),无需处理。 - Available 低 + Pages Input/sec 持续大于 0 + 磁盘队列高:真内存瓶颈,系统在疯狂换页。加内存或限制应用内存上限。
- Pool Nonpaged Bytes 单调增长:内核内存泄漏。定位方法是观察哪个进程/驱动在上涨,常见于杀软、备份 Agent、老版本网卡驱动,需联系厂商升级。
- Handle Count 单调增长:应用句柄泄漏,重启可临时缓解但会复发,需开发侧修复。
- IIS 站点 CPU 高:用
\Process(w3wp*)\% Processor Time定位到具体应用池,再配合appcmd list wp把进程 ID 对应到应用池名称。 - CPU 不高却很卡:瓶颈多半在磁盘(
\PhysicalDisk(_Total)\Avg. Disk sec/Transfer> 20ms 即偏慢)或数据库锁,不要只盯 CPU。
REM 把 w3wp 进程 ID 映射到应用程序池(IIS 场景必备) %windir%\system32\inetsrv\appcmd.exe list wp
六、落地步骤清单
- 用
perfmon /res做快速体检,确认瓶颈在 CPU、内存、磁盘还是网络。 - 用
Get-Counter -ListSet确认本机计数器名称(中英文系统名称不同,脚本要适配)。 - 选定核心计数器组合(CPU 3 项 + 内存 5 项 + 磁盘 2 项即可,不要贪多)。
- 用
logman create counter建立数据收集器集,采样间隔 30 秒,输出到非系统盘,启用 bincirc 循环。 - 用
schtasks配置自启动,确保重启后采集不中断。 - 配置告警阈值(CPU 持续 85% 超 5 分钟、Available 内存低于 10%、磁盘剩余 15%)。
- 每周用
relog导出 CSV,观察趋势而非单点,提前发现容量拐点。 - 把基线数据存档,作为扩容决策和故障对比的依据。
小结与避坑建议
Windows 服务器监控 CPU 和内存,工具链是清晰的:临时看用 perfmon /res,精确度量用性能监视器计数器,长期采集用 logman 数据收集器集,脚本化用 typeperf 与 Get-Counter。真正决定水平的是看哪些计数器、用什么阈值判断。
最常见的几个误区:
- 把“内存占用百分比”当告警指标。Windows 会主动用空闲内存做缓存,90% 占用可能非常健康。正确的指标是 Available MBytes 和 Pages Input/sec。
- 用工作集判断进程内存。工作集包含共享页,多个 .NET/IIS 进程会重复计算。应该用专用工作集或提交大小。
- 只看 CPU 百分比不看队列长度。百分比是瞬时值,队列长度才反映积压;多核机器上 CPU 50% 也可能已经排队。
- 采样间隔设太短。1 秒采样会产生海量数据,且采集本身消耗资源。30 秒足够做趋势分析,排障时临时用 5 秒。
- 性能日志放系统盘。日志增长很快,把 C 盘写满会引发连锁故障,建议单独数据盘并设 bincirc 上限。
- 重启后不再采集。数据收集器集默认会随系统启动,但用脚本方式启动的必须配计划任务,否则重启即断。
- 忽略基线。没有正常时期的基线数据,故障时你无法判断“这个值是高还是正常”。上线即建基线,是最划算的运维投入。
企业QQ咨询




