Created
November 20, 2025 12:53
-
-
Save NikiforovAll/64a5fbe2e1ae48d771fdd36537fab8af to your computer and use it in GitHub Desktop.
Measure-ProcessMemory.ps1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env pwsh | |
| <# | |
| .SYNOPSIS | |
| Measures the peak working set memory of a process. | |
| .DESCRIPTION | |
| Starts a process and reports its peak working set memory usage after completion. | |
| Similar to the 'time' command but focuses on memory metrics. | |
| .PARAMETER ProcessPath | |
| The path to the executable to run. | |
| .PARAMETER Arguments | |
| Optional arguments to pass to the process. | |
| .PARAMETER Unit | |
| Memory unit for output: 'MB' (default), 'GB', or 'KB'. | |
| .EXAMPLE | |
| .\Measure-ProcessMemory.ps1 -ProcessPath ".\test.exe" | |
| .\Measure-ProcessMemory.ps1 ".\test.exe" arg1 arg2 | |
| .\Measure-ProcessMemory.ps1 ".\app.exe" -verbose --config settings.json | |
| .\Measure-ProcessMemory.ps1 ".\app.exe" arg1 arg2 -Unit GB | |
| .NOTES | |
| The script displays memory metrics after the process exits. | |
| Arguments can be passed directly without the -Arguments flag. | |
| #> | |
| param( | |
| [Parameter(Mandatory=$true, Position=0, HelpMessage="Path to the executable")] | |
| [string]$ProcessPath, | |
| [Parameter(Mandatory=$false, Position=1, ValueFromRemainingArguments=$true, HelpMessage="Arguments to pass to the process")] | |
| [string[]]$Arguments, | |
| [Parameter(Mandatory=$false, HelpMessage="Memory unit: MB, GB, or KB")] | |
| [ValidateSet("MB", "GB", "KB")] | |
| [string]$Unit = "MB" | |
| ) | |
| # Validate that the process exists (check PATH and local files) | |
| $resolvedPath = $null | |
| if (Test-Path $ProcessPath) { | |
| $resolvedPath = $ProcessPath | |
| } else { | |
| $resolvedPath = (Get-Command $ProcessPath -ErrorAction SilentlyContinue).Source | |
| } | |
| if (-not $resolvedPath) { | |
| Write-Error "Process not found: $ProcessPath" | |
| exit 1 | |
| } | |
| Write-Host "Starting process: $ProcessPath" | |
| if ($Arguments) { | |
| Write-Host "Arguments: $Arguments" | |
| } | |
| Write-Host "" | |
| # Track execution time | |
| $startTime = Get-Date | |
| # Start the process | |
| try { | |
| if ($Arguments -and $Arguments.Count -gt 0) { | |
| $process = Start-Process -FilePath $resolvedPath -ArgumentList $Arguments -PassThru -NoNewWindow | |
| } else { | |
| $process = Start-Process -FilePath $resolvedPath -PassThru -NoNewWindow | |
| } | |
| } catch { | |
| Write-Error "Failed to start process: $_" | |
| exit 1 | |
| } | |
| $processId = $process.Id | |
| Write-Host "Process ID: $processId" | |
| # Monitor memory usage while the process is running | |
| $peakWorkingSet = 0 | |
| $peakVirtualMemory = 0 | |
| # Immediate first reading to catch short-lived processes | |
| try { | |
| $currentProcess = Get-Process -Id $processId -ErrorAction Stop | |
| $peakWorkingSet = $currentProcess.WorkingSet64 | |
| $peakVirtualMemory = $currentProcess.VirtualMemorySize64 | |
| } catch { | |
| # Process already exited | |
| } | |
| # Continue monitoring | |
| while (-not $process.HasExited) { | |
| try { | |
| $currentProcess = Get-Process -Id $processId -ErrorAction Stop | |
| $currentWorkingSet = $currentProcess.WorkingSet64 | |
| $currentVirtualMemory = $currentProcess.VirtualMemorySize64 | |
| if ($currentWorkingSet -gt $peakWorkingSet) { | |
| $peakWorkingSet = $currentWorkingSet | |
| } | |
| if ($currentVirtualMemory -gt $peakVirtualMemory) { | |
| $peakVirtualMemory = $currentVirtualMemory | |
| } | |
| Start-Sleep -Milliseconds 10 | |
| } catch { | |
| # Process might have exited | |
| break | |
| } | |
| } | |
| # Final check for peak values from the process object | |
| try { | |
| $process.Refresh() | |
| if ($process.PeakWorkingSet64 -gt $peakWorkingSet) { | |
| $peakWorkingSet = $process.PeakWorkingSet64 | |
| } | |
| if ($process.PeakVirtualMemorySize64 -gt $peakVirtualMemory) { | |
| $peakVirtualMemory = $process.PeakVirtualMemorySize64 | |
| } | |
| } catch { | |
| # Use the values we monitored | |
| } | |
| # Calculate execution time | |
| $endTime = Get-Date | |
| $executionTime = $endTime - $startTime | |
| # Convert to requested unit | |
| $divisor = switch ($Unit) { | |
| "MB" { 1MB } | |
| "GB" { 1GB } | |
| "KB" { 1KB } | |
| } | |
| $peakWorkingSetConverted = [math]::Round($peakWorkingSet / $divisor, 2) | |
| $peakVirtualMemoryConverted = [math]::Round($peakVirtualMemory / $divisor, 2) | |
| # Display results | |
| Write-Host "" | |
| Write-Host "=== Memory Statistics ===" | |
| Write-Host "Peak Working Set: $peakWorkingSetConverted $Unit" | |
| Write-Host " * Physical RAM used by the process at its peak" | |
| Write-Host "" | |
| Write-Host "Peak Virtual Memory: $peakVirtualMemoryConverted $Unit" | |
| Write-Host " * Total address space reserved (physical + virtual/paged memory)" | |
| Write-Host "" | |
| Write-Host "=== Execution Statistics ===" | |
| Write-Host "Execution Time: $($executionTime.TotalSeconds) seconds" | |
| Write-Host "Exit Code: $($process.ExitCode)" | |
| Write-Host "" | |
| Write-Host "* Hint: Working Set shows actual RAM usage, while Virtual Memory includes" | |
| Write-Host " memory that may be paged to disk. Working Set is typically the more" | |
| Write-Host " relevant metric for understanding real memory consumption." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment