How to Monitor VM Memory Pressure with PowerShell

Save to My DOJO

How to Monitor VM Memory Pressure with PowerShell

In a previous article, I gave you an introduction to using performance counters with Hyper-V and PowerShell. Of course you can use performance counters to manage all sorts of things, but personally, memory is the most critical. Even though I’m running Hyper-V in a non-production setting and don’t have access to massive server farms and back-end storage, the concepts that I am discussing will still apply to those instances. So even though this article is going to look specifically at Dynamic Memory counters, you might need something else. I encourage you, as with all of my content, to look at the techniques and concepts and not focus entirely on the end result.

Finding Instances

I am running Hyper-V on my Windows 10 box and will be querying the local computer. But there’s no reason this won’t work with a remote Hyper-V server. Although I have not tested anything with Window Server 2019. To keep the code re-usable I’ll define a variable for the Hyper-V host. You naturally will set it to whatever you need.

$Computer = $env:COMPUTERNAME

I want to check for counter instances on a single virtual machine. In the previous article, I talked about getting counter data from instances so I’m going to use a regular expression pattern to retrieve the instances associated with a given VM.

$VMName = "WIN10"
[regex]$rx = "(?<=\\).*(?=\)"

$c = get-counter -listset 'Hyper-V Dynamic Memory VM' -ComputerName $computer | 
Select -expand PathsWithInstances | where {$_ -match $VMName}

This is what I get.

Dynamic Memory perfomance counters

I can pass this array of counters to the Get-Counter cmdlet.

Getting counter data

There are a few more but you get the idea.

Monitoring Performance Counters

One of the nice features in Get-Counter is that you can watch a given set of counters. I will trust that you will read full cmdlet help and examples. But I can run this command to collect 10 samples of the specified counters, taking a reading every 2 seconds.

Get-Counter -counter $c -SampleInterval 2 -MaxSamples 10

Don’t get too gung-ho on your sampling interval.

The PowerShell output is far from user-friendly. You might want to save all the output into a variable or export to an XML file. Don’t use CSV. Or, let’s have PowerShell format the results into something a bit more manageable.

Get-Counter -Counter $c | 
Select-object -expandproperty Countersamples |
Sort-object -property InstanceName | 
Select-object -property InstanceName, 
@{Name="Counter";Expression={Split-path $_.path -Leaf}},
Cookedvalue,timestamp,
@{Name="VMHost";Expression={$rx.Match((Split-Path $_.path)).value.ToUpper()}} |
Format-Table

This is much easier to read.

Formatted performance counters in PowerShell

Or send it to Out-Gridview.

Get-Counter -Counter $c | 
Select-object -expandproperty Countersamples |
Sort-object -property InstanceName | 
Select-object -property InstanceName, 
@{Name="Counter";Expression={Split-path $_.path -Leaf}},
Cookedvalue,timestamp,
@{Name="VMHost";Expression={$rx.Match((Split-Path $_.path)).value.ToUpper()}} |
Out-GridView -Title "Dynamic Memory Counters"

PowerShell performance counters in Out-Gridview

Of course, none of this is actual monitoring. Here’s a code sample that achieves the same result as my earlier snippet: 10 samples at a 2-second interval.

1..10 | foreach {
    $data = Get-Counter -Counter $c | Select-object -expandproperty Countersamples |
    Sort-object -property InstanceName
    clear-Host
    $data | Select-object -property InstanceName, 
    @{Name="Counter";Expression={Split-path $_.path -Leaf}},
    Cookedvalue,timestamp,
    @{Name="VMHost";Expression={$rx.Match((Split-Path $_.path)).value.ToUpper()}} |
    Format-Table
    Start-sleep -Seconds 2
}

In this example, I am using the PowerShell console window as a monitoring tool. The performance data is written to the console with a refresh every 2 seconds.

Another option is to use my ConvertTo-WPFGrid function which is part of my PSScriptTools module which you can install from the PowerShell Gallery. With this command, I can display the performance counter data in a graphical form that will automatically refresh itself. Here’s a complete code snippet.

$computer = $env:COMPUTERNAME
[regex]$rx = "(?<=\\).*(?=\)"

$VMName = "SRV2"
#get the available counters
$c = get-counter -listset 'Hyper-V Dynamic Memory VM' -ComputerName $computer | 
Select -expand PathsWithInstances | where {$_ -match $VMName} 

1..5 | foreach {
    Get-Counter -Counter $c | Select-object -expandproperty Countersamples |
    Sort-object -property InstanceName | 
    Select-object -property InstanceName, 
    @{Name="Counter";Expression={Split-path $_.path -Leaf}},
    Cookedvalue,TimeStamp,
    @{Name="VMHost";Expression={$rx.Match((Split-Path $_.path)).value.ToUpper()}} |
    ConvertTo-WPFGrid -Title "Dynamic Memory Counters" -Timeout 10 -Height 300 -Width 600 -CenterScreen
}

When run I get something like this:

Display performance counters in a WPF form

My code snippet is getting 5 samples every 10 seconds. You could also use a Do Loop or other techniques to achieve the same effect.

Fine-Tuned Performance Gathering

Before I wrap up for today let me leave you with some techniques for fine-tuning the capture of performance counter data.  Let’s say that I am interested in the Pressure counters for a single virtual machine. I can fine tune the regular expression pattern to retrieve just those performance counter instances.

$computer = $env:COMPUTERNAME
$VMName = "SRV2"
[regex]$rx = "(?<=\\).*($VMName)\.*Pressure"

#get the available counters
get-counter -listset 'Hyper-V Dynamic Memory VM' -ComputerName $computer | 
Select -expand PathsWithInstances | where {$_ -match $rx}

Getting a subset of performance counters

Using this as the foundation and with some additional regular expression voodoo, I can build a little bit of monitoring code for a single virtual machine.

$computer = $env:COMPUTERNAME
$VMName = "SRV2"
[regex]$rx = "(?<=\\).*($VMName)\.*Pressure"
[regex]$rxCounter = "(?<=\\).*(?=\)"

#get the available counters
$c = get-counter -listset 'Hyper-V Dynamic Memory VM' -ComputerName $computer | 
Select -expand PathsWithInstances | where {$_ -match $rx} 

for ($i=0;$i -lt 12;$i++) {
    Get-Counter -Counter $c | Select-object -expandproperty Countersamples |
    Sort-Object -property Path |  
    Select-Object -property @{Name="VMHost";Expression={$rxCounter.Match((Split-Path $_.path)).value.ToUpper()}},
    @{Name="VMName";Expression = {$_.InstanceName.toUpper()}}, 
    @{Name="Counter";Expression={Split-Path $_.path -Leaf}},
    Cookedvalue,TimeStamp  |
    ConvertTo-WPFGrid -Title "Memory Pressure for $VMName" -Timeout 5 -Height 180 -Width 530 -CenterScreen
}

Finding the right dimensions takes a little bit of experimentation.

Memory Pressure Performance Data

Next time I’ll wrap up this series on Hyper-V performance counters in PowerShell with a technique I think you’ll find intriguing.

If you’re having problems with your PowerShell scripts use this guide to troubleshoot, diagnose problems and solve your PowerShell woes.

Threat Monitor
Share this post

Not a DOJO Member yet?

Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!

Leave a comment or ask a question

Your email address will not be published. Required fields are marked *

Your email address will not be published. Required fields are marked *

Notify me of follow-up replies via email

Yes, I would like to receive new blog posts by email

What is the color of grass?

Please note: If you’re not already a member on the Dojo Forums you will create a new account and receive an activation email.