Windows服务器如何监控CPU和内存?性能监视器实操指南

业务反馈“系统很卡”,你远程连上去打开任务管理器,看到 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 循环模式可避免日志把磁盘写满;日志目录务必放在非系统盘。

五、典型问题的判读套路

  1. CPU 高,但 % Processor Time 里 Privileged 占比大:不是应用算得多,而是内核态开销——查 \System\Context Switches/sec、中断、驱动(DPC 时间)。
  2. Processor Queue Length 长期大于核心数:CPU 真饱和。要么优化热点代码,要么扩容;先看 Top 进程确认是不是单个进程吃满。
  3. 内存占用高但 Pages Input/sec ≈ 0、Available 充足:正常。Windows 用空闲内存做文件缓存(\Memory\Cache Bytes),无需处理。
  4. Available 低 + Pages Input/sec 持续大于 0 + 磁盘队列高:真内存瓶颈,系统在疯狂换页。加内存或限制应用内存上限。
  5. Pool Nonpaged Bytes 单调增长:内核内存泄漏。定位方法是观察哪个进程/驱动在上涨,常见于杀软、备份 Agent、老版本网卡驱动,需联系厂商升级。
  6. Handle Count 单调增长:应用句柄泄漏,重启可临时缓解但会复发,需开发侧修复。
  7. IIS 站点 CPU 高:用 \Process(w3wp*)\% Processor Time 定位到具体应用池,再配合 appcmd list wp 把进程 ID 对应到应用池名称。
  8. CPU 不高却很卡:瓶颈多半在磁盘(\PhysicalDisk(_Total)\Avg. Disk sec/Transfer > 20ms 即偏慢)或数据库锁,不要只盯 CPU。
REM 把 w3wp 进程 ID 映射到应用程序池(IIS 场景必备)
%windir%\system32\inetsrv\appcmd.exe list wp

六、落地步骤清单

  1. 用 perfmon /res 做快速体检,确认瓶颈在 CPU、内存、磁盘还是网络。
  2. 用 Get-Counter -ListSet 确认本机计数器名称(中英文系统名称不同,脚本要适配)。
  3. 选定核心计数器组合(CPU 3 项 + 内存 5 项 + 磁盘 2 项即可,不要贪多)。
  4. 用 logman create counter 建立数据收集器集,采样间隔 30 秒,输出到非系统盘,启用 bincirc 循环。
  5. 用 schtasks 配置自启动,确保重启后采集不中断。
  6. 配置告警阈值(CPU 持续 85% 超 5 分钟、Available 内存低于 10%、磁盘剩余 15%)。
  7. 每周用 relog 导出 CSV,观察趋势而非单点,提前发现容量拐点。
  8. 把基线数据存档,作为扩容决策和故障对比的依据。

小结与避坑建议

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 上限。
  • 重启后不再采集。数据收集器集默认会随系统启动,但用脚本方式启动的必须配计划任务,否则重启即断。
  • 忽略基线。没有正常时期的基线数据,故障时你无法判断“这个值是高还是正常”。上线即建基线,是最划算的运维投入。