Why System Performance Monitoring Matters
Windows Server 2022 hosts workloads that other systems depend on. A performance problem on a server — whether caused by excessive CPU usage, memory pressure, disk I/O saturation, or network congestion — directly impacts end users and dependent services. Effective performance monitoring lets you identify these problems proactively, before they become outages, and gives you objective data to justify infrastructure changes or capacity additions.
Windows Server 2022 includes several overlapping tools for performance analysis, ranging from lightweight at-a-glance views (Task Manager, Resource Monitor) to deep historical collection systems (Performance Monitor with Data Collector Sets, Windows Performance Recorder). Understanding when to use each tool is as important as knowing how to use them.
Performance Monitor (perfmon.msc)
Performance Monitor is the core tool for collecting and graphing Windows performance counters in real time and over time. Open it with:
perfmon.msc
Or from a PowerShell or Command Prompt window. The interface has three main nodes in the left pane: Monitoring Tools (live graph view), Data Collector Sets (for scheduled recording), and Reports (for viewing historical data).
To add counters to the live Performance Monitor graph, click the green plus (+) button in the toolbar (or press Ctrl+N). The Add Counters dialog appears. Select the performance object (e.g., Processor), then select the specific counter (e.g., % Processor Time) and the instance (e.g., _Total for aggregate, or a specific core number). Click Add, then OK.
The graph updates in real time at a one-second interval by default. You can change the sample interval, scroll mode, time range, and graph type (line, histogram, report) from the graph properties (right-click the graph, select Properties).
Essential Performance Counters
Windows exposes hundreds of performance objects and thousands of individual counters. The following counters are the most important for baseline server monitoring:
Processor: % Processor Time (_Total) — Aggregate CPU utilisation across all cores. Sustained values above 80% indicate a CPU bottleneck. Check individual cores (instances 0, 1, 2…) to see if load is evenly distributed.
Processor: % Privileged Time (_Total) — Time spent in kernel mode. High values (above 30-40% of total CPU time) can indicate driver issues, excessive system calls, or I/O subsystem stress.
System: Processor Queue Length — The number of threads waiting for CPU time. A sustained value above the number of logical processors indicates CPU saturation. A queue of 2+ per core is a clear bottleneck signal.
Memory: Available MBytes — Physical RAM available for immediate use. This should not fall below 10-15% of total RAM under normal operation.
Memory: Pages/sec — The rate at which pages are read from or written to disk due to memory pressure (paging). Any sustained non-zero value indicates physical RAM is insufficient for the current workload.
Memory: Page Faults/sec — Total page faults per second (includes soft faults, which are resolved from standby list). Distinguish from hard faults (Pages/sec) which require disk access.
PhysicalDisk: Avg. Disk Queue Length (_Total) — Average number of queued disk requests. A value consistently above 2 per physical disk indicates disk I/O saturation.
PhysicalDisk: Avg. Disk sec/Transfer — Average latency per disk operation. For HDDs, values above 20ms indicate problems. For SSDs, values above 5ms are concerning. NVMe drives should be well under 1ms.
PhysicalDisk: Disk Bytes/sec — Throughput. Compare against the disk’s rated sequential read/write speed to determine saturation level.
Network Interface: Bytes Total/sec — Network throughput on each adapter. Compare against the adapter’s rated speed (1 Gbps = 125 MB/sec maximum).
Network Interface: Output Queue Length — Packets waiting to be sent. Non-zero sustained values indicate network saturation.
Adding Counters with PowerShell (Get-Counter)
The Get-Counter cmdlet retrieves performance counter values from the command line or from scripts. This is far more useful than the GUI for automation, alerting scripts, and collecting data from remote servers.
Get a single counter value:
Get-Counter -Counter "Processor(_Total)% Processor Time"
Get multiple counters in one call:
Get-Counter -Counter @(
"Processor(_Total)% Processor Time",
"MemoryAvailable MBytes",
"MemoryPages/sec",
"PhysicalDisk(_Total)Avg. Disk Queue Length"
)
Sample a counter every 5 seconds for 60 samples and save to a CSV:
Get-Counter -Counter "Processor(_Total)% Processor Time" `
-SampleInterval 5 -MaxSamples 60 |
ForEach-Object { $_.CounterSamples } |
Select-Object Timestamp, Path, CookedValue |
Export-Csv -Path "C:PerfDatacpu_$(Get-Date -Format yyyyMMdd_HHmm).csv" -NoTypeInformation
List all available performance objects on the local system:
Get-Counter -ListSet * | Select-Object CounterSetName, Description | Sort-Object CounterSetName
List all counters in a specific performance object:
Get-Counter -ListSet "PhysicalDisk" | Select-Object -ExpandProperty Counter
Collect counters from a remote server:
Get-Counter -ComputerName SRV-WEB01 -Counter "Processor(_Total)% Processor Time"
typeperf Command-Line Tool
typeperf is a legacy command-line tool that writes counter data to the console or a file. It is simpler than Get-Counter for quick sampling from cmd.exe or in batch scripts.
Display CPU and memory in real time (samples every 1 second, 10 times):
typeperf "Processor(_Total)% Processor Time" "MemoryAvailable MBytes" -sc 10
Write output to a CSV file with a 5-second interval for 100 samples:
typeperf "Processor(_Total)% Processor Time" "PhysicalDisk(_Total)Disk Bytes/sec" -si 5 -sc 100 -f CSV -o C:PerfDataoutput.csv
Read counter names from a text file (one counter per line):
typeperf -cf C:Scriptscounters.txt -si 10 -sc 360 -f CSV -o C:PerfDatahourly.csv
Data Collector Sets
Data Collector Sets (DCS) allow you to schedule automatic performance data collection over a defined period. This is essential for creating baselines and for capturing data during a reported issue that occurs at a specific time (such as a nightly batch job).
To create a DCS in the GUI, expand Data Collector Sets > User Defined in Performance Monitor, right-click and select New > Data Collector Set. Choose Create manually (Advanced) to specify exact counters, sample intervals, and output formats.
Create a DCS from PowerShell using logman.exe (the command-line interface for Data Collector Sets):
logman create counter "BaselineCollect" `
-c "Processor(_Total)% Processor Time" `
"MemoryAvailable MBytes" `
"MemoryPages/sec" `
"PhysicalDisk(_Total)Avg. Disk Queue Length" `
"PhysicalDisk(_Total)Avg. Disk sec/Transfer" `
"Network Interface(*)Bytes Total/sec" `
-si 15 `
-f bin `
-o "C:PerfDataBaselinebaseline" `
-max 512 `
-cnf 01:00:00
Start and stop the DCS on demand:
logman start "BaselineCollect"
logman stop "BaselineCollect"
Schedule the DCS to run automatically at 08:00 every weekday for two hours:
logman update "BaselineCollect" -b 08:00:00 -e 10:00:00 -r
logman start "BaselineCollect" -as
Convert a binary (.blg) data file to CSV for analysis:
relog "C:PerfDataBaselinebaseline_000001.blg" -f CSV -o "C:PerfDataBaselinebaseline.csv"
Once you have the CSV, you can import it into Excel or process it with PowerShell to compute averages, maximums, and percentile values for each counter over the collection period.
Resource Monitor
Resource Monitor provides a more detailed real-time view than Task Manager, broken out by CPU, memory, disk, and network tabs. Open it with:
resmon.exe
Resource Monitor is particularly useful for disk and network analysis. The Disk tab shows which processes are reading/writing to disk and which files they are accessing. The Network tab shows which processes have open network connections and their bandwidth usage. This makes it easy to identify which application is responsible for a sustained disk or network load that appears as a spike in Performance Monitor.
The Memory tab in Resource Monitor shows the Standby, Modified, Free, and In Use memory breakdown. A large Standby pool is normal and healthy — Windows aggressively caches data in Standby. The concerning values are high Modified (data waiting to be written to disk) and very low Free + Standby (indicates genuine memory pressure).
Task Manager Performance Tab
Task Manager is not a substitute for Performance Monitor, but its Performance tab provides an excellent at-a-glance summary. Open Task Manager with:
taskmgr.exe
The Performance tab shows sparkline graphs for CPU, Memory, each disk, and each network adapter. Clicking any of these opens a detailed view. The CPU view shows utilisation, speed, logical processors, processes, threads, handles, and uptime. The Memory view shows the used/available/committed/cached breakdown. The disk view shows both read and write throughput and I/O latency.
For a quick performance snapshot from the command line without opening a GUI, use the tasklist command:
tasklist /v /fo CSV | ConvertFrom-Csv | Sort-Object "Mem Usage" -Descending | Select-Object -First 10 "Image Name", "PID", "Mem Usage" | Format-Table
Windows Performance Recorder
Windows Performance Recorder (WPR) is a more advanced ETW (Event Tracing for Windows) based recording tool included in the Windows Assessment and Deployment Kit. It captures extremely detailed system traces — CPU scheduling, disk I/O, memory allocation, and more — that can be analysed in Windows Performance Analyzer (WPA).
WPR is most useful for diagnosing intermittent or transient performance problems that are difficult to capture with polling-based tools. Start a recording from an elevated command prompt:
wpr -start CPU -start DiskIO -start Network -filemode
Stop the recording and save the ETL trace file:
wpr -stop C:PerfTracestrace_$(Get-Date -Format yyyyMMdd_HHmm).etl
Available profiles include CPU, DiskIO, FileIO, Network, Heap, Pool, VirtualAllocation, and others. Combine them as needed. The resulting .etl file can be opened in Windows Performance Analyzer (WPA.exe), which provides flame graphs, timeline views, and drill-down capabilities unavailable in Performance Monitor.
Identifying Common Performance Bottlenecks
Combining the counters from Performance Monitor with the process-level detail from Resource Monitor enables systematic bottleneck identification.
CPU bottleneck: Processor% Processor Time (_Total) is consistently above 80% AND SystemProcessor Queue Length exceeds the number of logical processors. Next step: identify which process is consuming CPU using Task Manager or Get-Process, then investigate the workload or consider vertical scaling.
Memory pressure: MemoryAvailable MBytes drops below 10% of total RAM AND MemoryPages/sec is non-zero. Next step: identify which process has the largest working set using Resource Monitor’s memory tab, then investigate for memory leaks, misconfiguration, or add physical RAM.
Disk I/O saturation: PhysicalDiskAvg. Disk Queue Length exceeds 2 per disk AND PhysicalDiskAvg. Disk sec/Transfer exceeds acceptable latency for the storage type. Next step: use Resource Monitor to identify which processes are generating the I/O, then evaluate caching, storage tier upgrades (SSD), or workload redistribution.
Network saturation: Network InterfaceBytes Total/sec approaches the link speed AND Output Queue Length is non-zero. Next step: identify which process is sending/receiving using Resource Monitor Network tab, then consider link aggregation, upgrading to 10GbE, or offloading traffic to dedicated interfaces.
Build performance baselines during known-good periods by running a Data Collector Set for a full business week. These baselines give you objective reference data to compare against when a performance problem is reported later, turning subjective complaints into measurable deviations.