Why Monitor Hyper-V with Performance Counters?

Performance counters are the foundation of Hyper-V health monitoring on Windows Server 2022. They expose real-time and historical metrics for every layer of the virtualisation stack: hypervisor scheduling, virtual processor utilisation, memory pressure, virtual storage I/O, and virtual network throughput. Without monitoring these counters, capacity planning is guesswork and performance problems are diagnosed reactively rather than proactively.

Windows Server 2022 ships with a rich set of Hyper-V-specific performance counter sets that are distinct from the standard Windows CPU and disk counters. The Hyper-V counters measure activity at the hypervisor layer, which is often invisible to the standard Windows Task Manager or Resource Monitor running inside the management OS partition.

Key Hyper-V Performance Counter Sets

Understanding which counter sets to use for each type of question is essential. The following are the most important Hyper-V counter sets on Windows Server 2022.

Hyper-V Hypervisor Virtual Processor. This counter set measures CPU activity for each virtual processor assigned to each VM and to the hypervisor root partition. The key counter is % Guest Run Time, which shows the percentage of time a virtual processor spends executing VM (guest) code. % Hypervisor Run Time shows time spent in the hypervisor itself on behalf of that VP. % Total Run Time is the sum. High % Hypervisor Run Time relative to % Guest Run Time indicates virtualisation overhead from frequent VM exits.

Hyper-V Hypervisor Logical Processor. This counter set measures the activity of each logical processor (physical CPU thread) from the hypervisor’s perspective. % Total Run Time shows total physical CPU utilisation. % Guest Run Time shows how much of that time is spent running guest VMs. If % Total Run Time is consistently above 85-90% across all logical processors, the host is approaching CPU saturation.

Hyper-V Virtual Storage Device. Exposes per-VM, per-disk I/O metrics. Key counters include Read Bytes/sec, Write Bytes/sec, Read Operations/sec, Write Operations/sec, and critically, Normalised Throughput which normalises different I/O sizes to equivalent 8 KB operations for easier comparison.

Hyper-V Virtual Network Adapter. Provides per-VM, per-virtual NIC throughput metrics: Bytes Received/sec, Bytes Sent/sec, Packets Received/sec, Packets Sent/sec, and Dropped Packets Incoming/sec. Dropped packets on a virtual NIC typically indicate the virtual switch is under pressure or the physical NIC is saturated.

Hyper-V Dynamic Memory VM. If Dynamic Memory is enabled, this counter set tracks Current Pressure, Guest Available Memory, Physical Memory (currently assigned), and Memory Add Operations/sec. Current Pressure above 100 indicates the VM is requesting more memory than Hyper-V is giving it — a sign of memory contention.

Adding Hyper-V Counters in Performance Monitor

Performance Monitor (perfmon.exe) provides a graphical interface for viewing performance counters in real time and logging them for historical analysis. To add Hyper-V counters:

1. Open Performance Monitor: press Win+R, type perfmon, press Enter.

2. In the left pane, expand Monitoring Tools and select Performance Monitor.

3. Click the green plus (+) button in the toolbar to open the Add Counters dialog.

4. In the “Available counters” list, scroll to the Hyper-V counter sets. Expand Hyper-V Hypervisor Virtual Processor, select % Guest Run Time, then choose instances from the list on the right (each instance corresponds to a specific VP of a specific VM, formatted as VMname:VP #).

5. Click Add, then repeat for other counters. Click OK to close the dialog and begin graphing.

For sustained monitoring, create a Data Collector Set to log counter values at defined intervals to a .blg file:

In Performance Monitor, right-click Data Collector Sets > User Defined > New > Data Collector Set. Follow the wizard, selecting “Create manually (Advanced)”, then “Performance counter”, and add the desired Hyper-V counters. Set the sample interval (60 seconds is reasonable for capacity trending) and configure a schedule.

Using Get-Counter for Hyper-V Metrics in PowerShell

The Get-Counter cmdlet retrieves performance counter values programmatically, making it straightforward to incorporate Hyper-V monitoring into scripts, scheduled tasks, and custom dashboards.

List all available Hyper-V counter sets:

Get-Counter -ListSet "Hyper-V*" | Select-Object CounterSetName, CounterSetType | Sort-Object CounterSetName

Get all instances and values for virtual processor guest run time:

Get-Counter "Hyper-V Hypervisor Virtual Processor(*)% Guest Run Time" | 
  Select-Object -ExpandProperty CounterSamples | 
  Select-Object InstanceName, CookedValue |
  Sort-Object CookedValue -Descending

Get virtual storage device write latency for all VMs (latency is expressed in microseconds in the raw counter; multiply by 0.001 for milliseconds if needed):

Get-Counter "Hyper-V Virtual Storage Device(*)Write Operations/sec",
            "Hyper-V Virtual Storage Device(*)Read Operations/sec" |
  Select-Object -ExpandProperty CounterSamples |
  Where-Object {$_.CookedValue -gt 0} |
  Select-Object InstanceName, Path, @{N="Value";E={[math]::Round($_.CookedValue,1)}} |
  Sort-Object Value -Descending

Collect samples at a 5-second interval for 12 samples (1 minute of data) and export to CSV for analysis:

$counters = @(
    "Hyper-V Hypervisor Logical Processor(_Total)% Total Run Time",
    "Hyper-V Virtual Network Adapter(*)Bytes Received/sec",
    "Hyper-V Virtual Network Adapter(*)Bytes Sent/sec",
    "Hyper-V Dynamic Memory VM(*)Current Pressure"
)

$results = Get-Counter -Counter $counters -SampleInterval 5 -MaxSamples 12
$results.CounterSamples | Export-Csv -Path "C:PerfDatahyper-v-$(Get-Date -Format yyyyMMdd-HHmm).csv" -NoTypeInformation

Interpreting Virtual Processor Utilisation

Virtual processor utilisation counters require careful interpretation because they measure activity at the hypervisor level, not from within the VM’s guest OS perspective.

The % Guest Run Time counter under Hyper-V Hypervisor Virtual Processor measures the proportion of real time that the virtual processor is actively executing guest code. A value of 80% does not necessarily mean the VM’s application is consuming 80% of a CPU core — it means the virtual processor was scheduled and running on a physical CPU thread for 80% of the measurement interval.

The % Hypervisor Run Time counter measures time spent in the hypervisor root handling VM exits on behalf of that VP. A VM generating many I/O operations, memory-mapped file accesses, or hardware interrupt injections will have a higher % Hypervisor Run Time. If this counter is consistently above 10-15% for a VP, investigate the VM’s I/O and interrupt activity.

CPU ready time — the time a virtual processor is runnable but waiting for a physical CPU thread — is not directly exposed as a single counter in Hyper-V the way it is in VMware. However, you can infer it by comparing the sum of all VP % Total Run Time values to the physical CPU thread count. If the sum significantly exceeds the number of logical processors, VPs are contending for CPU time:

# Measure total VP demand vs available physical threads
$vpUtilisation = Get-Counter "Hyper-V Hypervisor Virtual Processor(*)% Total Run Time" |
  Select-Object -ExpandProperty CounterSamples |
  Where-Object {$_.InstanceName -ne "_total"} |
  Measure-Object -Property CookedValue -Sum

$logicalProcessors = (Get-ComputerInfo).CsNumberOfLogicalProcessors
$overcommitRatio = $vpUtilisation.Sum / ($logicalProcessors * 100)

Write-Host "Total VP demand: $([math]::Round($vpUtilisation.Sum / 100, 2)) logical processor equivalents"
Write-Host "Physical logical processors: $logicalProcessors"
Write-Host "Overcommit ratio: $([math]::Round($overcommitRatio, 2))x"

A ratio above 0.8 indicates the host is approaching CPU saturation. Above 1.0 means VPs are competing for physical CPU time, and application latency inside VMs will increase.

Storage Latency Counters

High storage latency inside a VM is one of the most common causes of application performance degradation. The Hyper-V Virtual Storage Device counter set provides several latency-related metrics.

Maximum Write Latency and Maximum Read Latency report the worst-case latency observed during the measurement interval, in microseconds. These counters are useful for detecting latency spikes that would not be visible in average metrics.

Check all VM storage latencies in real time:

Get-Counter "Hyper-V Virtual Storage Device(*)Maximum Write Latency",
            "Hyper-V Virtual Storage Device(*)Maximum Read Latency" |
  Select-Object -ExpandProperty CounterSamples |
  Select-Object InstanceName, 
    @{N="MetricMs";E={[math]::Round($_.CookedValue / 1000, 2)}},
    Path |
  Where-Object {$_.MetricMs -gt 5} |
  Sort-Object MetricMs -Descending |
  Format-Table -AutoSize

Latency thresholds to act on: read latency consistently above 20 ms or write latency consistently above 20 ms for an OLTP database virtual disk indicates a storage subsystem problem. For spinning disk (HDD) storage backing VHDX files, expect latencies of 5–20 ms. For SSD storage, expect sub-5 ms. For NVMe, expect sub-1 ms. If you are seeing latencies much higher than these baselines, investigate the host’s physical storage, the VHDX file’s physical disk location, and whether multiple VMs are contending for the same storage.

Memory Demand vs Available Counters

When Dynamic Memory is enabled for VMs, the Hyper-V Dynamic Memory VM counter set provides insight into memory pressure across the host’s VMs.

Current Pressure is a percentage expressing the ratio of the VM’s current memory demand to the amount Hyper-V is currently assigning it. A value below 100 means the VM has more memory than it is demanding. A value of 100 means demand equals assignment. A value above 100 means the VM is paging because it cannot get the memory it wants — this is a critical alert condition.

# Find VMs with memory pressure above 100 (actively paging due to memory starvation)
Get-Counter "Hyper-V Dynamic Memory VM(*)Current Pressure" |
  Select-Object -ExpandProperty CounterSamples |
  Where-Object {$_.InstanceName -ne "_total" -and $_.CookedValue -gt 100} |
  Select-Object InstanceName, @{N="Pressure%";E={[math]::Round($_.CookedValue, 1)}} |
  Sort-Object "Pressure%" -Descending

For VMs with Dynamic Memory disabled (static memory assignment), use the Memory counter set inside the VM’s guest OS to track available memory, or monitor the VM from the host using Process(vmwp)Working Set counters for the VM’s worker process. The host-level counter Hyper-V Dynamic Memory Balancer(*)Available Memory shows the amount of memory the balancer has available to give to demanding VMs.

Hyper-V Reporting in Windows Admin Center

Windows Admin Center (WAC) provides a graphical Hyper-V host dashboard that presents key performance counters without requiring Performance Monitor expertise. To access it, install WAC on Windows Server 2022 (download from the Microsoft Evaluation Center), then connect to the Hyper-V host in WAC’s browser interface.

Under the Virtual Machines section, WAC shows CPU utilisation, memory assignment, and network activity for each VM. The host dashboard shows aggregate CPU, memory, and storage metrics for the Hyper-V host. WAC reads these values from WMI and the performance counter infrastructure, so the data is sourced from the same underlying metrics described in this article.

For more sophisticated trend analysis and alerting, WAC integrates with Azure Monitor. Configuring the Azure Monitor agent on Windows Server 2022 allows you to forward performance counter data to a Log Analytics workspace where you can build KQL-based dashboards and alert rules.

Prometheus and Grafana with windows_exporter for Hyper-V Metrics

For teams running a Prometheus/Grafana monitoring stack, the windows_exporter agent (formerly WMI Exporter) can expose Hyper-V performance counters as Prometheus metrics. This enables you to build Grafana dashboards and Prometheus alerting rules for Hyper-V health.

Install windows_exporter on the Hyper-V host. The Hyper-V collector must be explicitly enabled:

# Download and install windows_exporter with Hyper-V collector enabled
# Run in PowerShell as Administrator on the Hyper-V host
$url = "https://github.com/prometheus-community/windows_exporter/releases/download/v0.27.2/windows_exporter-0.27.2-amd64.msi"
Invoke-WebRequest -Uri $url -OutFile "C:Toolswindows_exporter.msi"

msiexec /i "C:Toolswindows_exporter.msi" ENABLED_COLLECTORS="hyperv,cpu,memory,logical_disk,net" LISTEN_PORT=9182 /quiet

Verify the exporter is running and exposing metrics:

# Check the service is running
Get-Service windows_exporter

# Test the metrics endpoint (run on the Hyper-V host)
Invoke-WebRequest http://localhost:9182/metrics | Select-Object -ExpandProperty Content | Select-String "windows_hyperv"

Add the Hyper-V host to your Prometheus scrape configuration:

scrape_configs:
  - job_name: 'hyperv-host-01'
    static_configs:
      - targets: ['hyperv-host-01.internal.example.com:9182']
    metrics_path: /metrics
    scrape_interval: 30s

Key Prometheus metric names exposed by the windows_exporter Hyper-V collector include:

windows_hyperv_hypervisor_virtual_processor_guest_run_time_total
windows_hyperv_hypervisor_logical_processor_total_run_time_percent
windows_hyperv_virtual_storage_device_read_bytes_total
windows_hyperv_virtual_storage_device_write_bytes_total
windows_hyperv_virtual_storage_device_read_operations_total
windows_hyperv_virtual_storage_device_write_operations_total
windows_hyperv_virtual_network_adapter_bytes_received_total
windows_hyperv_virtual_network_adapter_bytes_sent_total
windows_hyperv_dynamic_memory_vm_current_pressure

Import the community Grafana dashboard for windows_exporter (dashboard ID 14694 or search Grafana Labs for “windows_exporter Hyper-V”) to get pre-built panels for all major Hyper-V metrics. Customise the panels to add alert thresholds appropriate for your environment — for example, alert when any VM’s Dynamic Memory current pressure exceeds 95, or when any logical processor’s total run time exceeds 90% for more than 5 minutes.

Set up Prometheus alerting rules for critical Hyper-V conditions:

groups:
  - name: hyperv_alerts
    rules:
      - alert: HypervMemoryPressureHigh
        expr: windows_hyperv_dynamic_memory_vm_current_pressure > 95
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Hyper-V VM memory pressure high on {{ $labels.instance }}"
          description: "VM {{ $labels.vm }} has memory pressure of {{ $value }}%"

      - alert: HypervHostCPUSaturation
        expr: avg(windows_hyperv_hypervisor_logical_processor_total_run_time_percent) > 90
        for: 10m
        labels:
          severity: critical
        annotations:
          summary: "Hyper-V host CPU near saturation on {{ $labels.instance }}"
          description: "Average logical processor utilisation is {{ $value }}%"