【PowerShell】CPU使用率が高いプロセスTop 5を特定!監視・調査に役立つ実践スクリプト解説

ポコぺん

Windows OSの運用管理において、避けては通れないトラブルの一つが「CPU使用率の高騰によるシステム遅延」です。
サーバーのレスポンスが極端に低下したり、特定のアプリケーションがフリーズしたりといった事態に直面した際、私たちは迅速に「犯人」となるプロセスを特定し、対処しなければなりません。

多くの場合、タスクマネージャーを開いて状況を確認しますが、リモート環境での調査や、定期的な監視ログの取得、あるいは自動復旧スクリプトへの組み込みを検討する場合、GUIでの操作には限界があります。
そこで活用したいのがPowerShellですが、標準的なGet-Processコマンドで取得できるCPUの値は「プロセス開始時からの累積時間」であるため、単発の実行では「今現在の負荷」を正確に知ることができないという落とし穴があります。

本記事では、この課題を解決するために、わずか数行で実装できる「CPU高負荷プロセス特定スクリプト」を解説します。
1秒間の差分を計測することで、リアルタイムに近いCPU使用率を算出し、負荷の高い上位5件を瞬時にリストアップする方法をご紹介します。
トラブルシューティングの時間を短縮し、スマートなシステム運用を実現するためのテクニックをぜひ習得してください。

はじめに

スクリプトによる「2点間計測」が最も正確に負荷を特定できる

Windows PowerShellでプロセス情報を取得する際、多くのユーザーが最初に手にするのはGet-Processコマンドです。
しかし、このコマンドが標準で返すCPUプロパティの数値には、運用上の大きな注意点があります。
この数値は「現時点でのCPU使用率」ではなく、「プロセスが起動してから現在までに消費したCPU時間の合計(秒単位)」を示しているからです。

例えば、起動から数日間経過しているプロセスの場合、その時点では全くCPUを消費していなくても、累積のCPU時間は非常に大きな値として表示されます。
これをそのままソートして上位を抽出しても、「今、システムを圧迫しているプロセス」を特定することはできません。

なぜ「2回取得」して「差分」を見るのか

「今、何が起きているか」を正確に把握するためには、カメラのシャッタースピードのように、特定の時間枠を切り取って計測する必要があります。
提供されたスクリプトが採用している「2点間計測」のロジックは以下の通りです。

  • 1回目のサンプリング(T1):
    Get-Processを実行し、その時点での各プロセスの累積CPU時間を取得して変数$sample1に格納します。
  • インターバルの挿入:
    Start-Sleep -Seconds 1コマンドを用いて、あえて1秒間の待機時間を設けます。
    この1秒間こそが、私たちが観測したい「現在の負荷」を測定するための期間となります。
  • 2回目のサンプリング(T2):
    待機後、再び全プロセスの累積CPU時間を取得し、変数$sample2に格納します。
  • 差分計算(Delta):
    2回目の値から1回目の値を差し引く($proc2.CPU - $proc1.CPU)ことで、この「1秒間という極めて短い期間に、そのプロセスがどれだけCPUを占有したか」を浮き彫りにします。

算出した数値の信頼性

このスクリプトでは、単に差分を出すだけでなく、実用性を高めるための処理が施されています。

  1. プロセスの同一性確認:
    $proc2 = $sample2 | Where-Object { $_.Id -eq $proc1.Id } という処理により、プロセスID(PID)を照合しています。
    これにより、1回目と2回目の計測の間に終了したプロセスや、新しく起動したプロセスを混同することなく、同一の個体に対して正確に計算を行うことができます。
  2. パーセンテージへの近似:
    差分(秒)に対して100を掛ける($delta * 100)ことで、直感的に理解しやすい「%」に近い形式へと変換しています。
  3. 視認性の向上:
    計算結果は[math]::Roundメソッドによって小数点第2位で四捨五入されており、ノイズの少ないクリアなデータとして出力されます。

このように、累積値という静的なデータから、時間的な変化という動的なデータを導き出す「2点間計測」こそが、スクリプトによるパフォーマンス調査において最も信頼性の高いアプローチとなります。

CPU負荷特定スクリプトの全容と仕組み

ここでは、実際に使用するPowerShellスクリプトのコードを分割し、それぞれのブロックがどのような役割を果たしているのかを詳しく解説します。
このスクリプトは、単に情報を表示するだけでなく、計算の精度と処理の軽さを両立させた設計になっています。

1. データの初回取得とフィルタリング

まず、現在のプロセス一覧から必要な情報だけを抽出します。

PowerShell

# 1回目の取得
$sample1 = Get-Process | Where-Object { $_.CPU } | ForEach-Object {
    [PSCustomObject]@{
        Name = $_.ProcessName
        Id = $_.Id
        CPU = $_.CPU
    }
}
  • Where-Object { $_.CPU }:
    CPU情報のプロパティを持っていないプロセス(アイドルプロセスなど)を除外することで、後続の計算エラーを防いでいます。
  • PSCustomObject:
    後の比較計算で使いやすいよう、「プロセス名」「ID」「累積CPU時間」の3項目に絞ったカスタムオブジェクトを作成してメモリに格納します。

2. 測定のための「静止時間」を作る

正確な負荷を測るために、あえて処理を一時停止させます。

PowerShell

Start-Sleep -Seconds 1  # 測定間隔(1秒)

この1秒間に各プロセスが消費したCPU時間が、今回の計測対象となります。
より長時間の平均負荷を知りたい場合は、この数値を「5」や「10」に変更するだけで対応可能です。

3. 2回目の取得と差分計算の実行

待機後、再度データを取得し、1回目との差分をループ処理で計算します。

PowerShell

$cpuDiff = foreach ($proc1 in $sample1) {
    $proc2 = $sample2 | Where-Object { $_.Id -eq $proc1.Id }
    if ($proc2) {
        $delta = $proc2.CPU - $proc1.CPU
        [PSCustomObject]@{
            Name = $proc1.Name
            Id = $proc1.Id
            CPU_Usage = [math]::Round($delta * 100, 2)
        }
    }
}
  • IDによる紐付け:
    プロセス名ではなく「Id(PID)」で照合を行うことで、同名の別プロセスが動いている環境でも、個体別の正確な数値を算出できます。
  • 数学的な整形:
    [math]::Round を用いて、人間が見て直感的に分かりやすい小数点第2位までの数値に丸めています。

このように、「取得 → 待機 → 再取得 → 比較」というステップを踏むことで、タスクマネージャーの「CPU」列に近い、動的な負荷状況をコマンドライン上で再現しています。

スクリプトの実装例:CPU使用率Top 5を表示する

計算によって導き出された $cpuDiff には、実行時に動いていたすべてのプロセスの負荷状況が格納されています。
ここから「本当に確認すべきプロセス」だけを絞り込むために、PowerShellの強力なパイプライン処理を活用します。

1. 実行結果の並び替えと上位抽出

以下のコマンドを実行することで、負荷の高い順にデータを整理し、トップ5のみを画面に表示します。

PowerShell

# 使用率順に並び替え & 上位5件表示
$cpuDiff | Sort-Object -Property CPU_Usage -Descending | Select-Object -First 5
  • Sort-Object -Property CPU_Usage -Descending:
    計算した CPU_Usage プロパティを基準に並び替えます。
    -Descending(降順)を指定することで、負荷の高いプロセスが一番上にくるように制御しています。
  • Select-Object -First 5:
    並び替えたリストの先頭から5つだけを取得します。
    これにより、コンソールが大量のデータで埋め尽くされるのを防ぎ、一目で異常なプロセスを特定できるようになります。

2. 自身のスクリプトが上位に来る場合の対処

このスクリプトを実行すると、計算処理そのものにCPUリソースを消費するため、実行結果の1位が「powershell(自分自身)」になってしまうことがあります。
もし純粋に他のアプリケーションの負荷だけを調査したい場合は、以下のテクニックが有効です。

PowerShell

# 1位(自分自身など)を除外して、2位から6位を表示する場合
$cpuDiff | Sort-Object -Property CPU_Usage -Descending | Select-Object -Skip 1 | Select-Object -First 5
  • Select-Object -Skip 1:
    リストの1番目(最も負荷が高いもの)をあえて読み飛ばします。
    これにより、監視ツール自体の負荷を除いた「真の調査対象」にフォーカスしたレポートを作成できます。

このように、パイプライン(|)を使ってコマンドを繋いでいく手法は、PowerShellにおけるデータ処理の基本であり、最も効率的な方法です。

実務で役立つカスタマイズと注意点

紹介したスクリプトは非常にシンプルですが、実務環境に合わせて調整することで、さらに強力なツールへと進化します。

1. 測定時間を調整して「平均値」の精度を上げる

サンプルコードでは Start-Sleep -Seconds 1 としていますが、CPU負荷には瞬間的なスパイク(一時的な跳ね上がり)がつきものです。

  • 1秒間の場合:
    瞬間的な負荷を捉えるのに適していますが、数値が変動しやすく、たまたまその瞬間に動いたプロセスが上位に来ることがあります。
  • 5〜10秒間に延ばす場合:
    数秒間の平均的な使用率を算出できるため、持続的に負荷をかけている「真の原因」を特定しやすくなります。
    環境に合わせて待機時間を調整してください。

2. 存在しないプロセスへのエラー対策

計測の1回目と2回目の間にプロセスが終了してしまうことは珍しくありません。
このスクリプトでは、if ($proc2) という条件分岐を入れることで、2回目のデータが存在する場合のみ計算を行うように設計されています。
これにより、計算対象のプロセスが途中で消えてしまった際のエラー出力を防ぎ、スクリプトの停止を回避しています。

3. 監視ログとして保存する

画面に表示するだけでなく、定期的に実行して結果をファイルに残したい場合は、末尾に Export-Csv を追加するのが効果的です。

PowerShell

$cpuDiff | Sort-Object -Property CPU_Usage -Descending | Select-Object -First 5 | Export-Csv -Path "C:\Logs\CPU_Usage_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv" -NoTypeInformation -Encoding UTF8

このように記述すれば、実行時のタイムスタンプをファイル名に含めた状態で、上位5件をCSV形式で保存できます。
後から負荷の原因を分析する際の貴重な資料となります。

4. 自身の負荷を除外する運用

前述の通り、自身のスクリプト(PowerShell)が計算処理で上位に来てしまうことがあります。
監視システムに組み込む際などは、Select-Object -Skip 1 を活用して「自分以外の高負荷プロセス」を確実にキャッチできるよう設定しておくのが実務上のコツです。

まとめ

PowerShellでリソース監視を自動化しよう

Windowsのパフォーマンス低下を引き起こす「高負荷プロセス」を特定する際、PowerShellを活用することで、手動操作(GUI)では得られない正確性と効率性を手に入れることができます。

本記事で解説したスクリプトのポイントを振り返ります。

  • 累積時間ではなく「差分」を見る:
    Get-Processの累積CPU時間から、1秒間のインターバルを設けて計算することで、真の「現在の負荷」を可視化しました。
  • プロセスの特定精度:
    プロセスIDによる照合を行うことで、同名プロセスが複数存在する場合でも、個体別の正確な負荷計算を可能にしました。
  • 柔軟なフィルタリング:
    自身のスクリプト負荷を除外したり、上位数件に絞り込んだりすることで、調査に必要な情報だけを即座に抽出しました。

リソース監視は、トラブルが起きてから行うだけでなく、日常的なヘルスチェックとして自動化することに真の価値があります。
今回紹介したスクリプトをタスクスケジューラに登録して定期実行させたり、閾値を超えた場合に警告メールを飛ばすような仕組みに発展させたりすることも可能です。

まずは、お使いの環境でこのスクリプトを実行し、システムの「健康状態」を数値で把握することから始めてみてください。
コマンド一行でシステムを制御できるPowerShellの力を借りれば、インフラ管理の質は確実に向上します。


参考スクリプト:

<#
.SYNOPSIS
    CPU使用率が高い上位5件のプロセスを特定します。

.DESCRIPTION
    Get-Processコマンドで取得できる累積CPU時間の差分を計測することで、
    現在のリアルタイムに近いCPU使用率を算出します。
    標準では1秒間のインターバルで計測を行い、負荷の高い順に表示します。

.PARAMETER Top
    表示するプロセス数を指定します(デフォルトは5)。

.PARAMETER Interval
    計測の間隔を秒単位で指定します(デフォルトは1)。

.EXAMPLE
    .\Get-TopCpuProcesses.ps1
    現在のCPU高負荷プロセスTop 5を表示します。

.NOTES
    【作成】20xx/xx/xx ポコぺん       新規作成
#>

# --- 設定 ---
$TopCount = 5      # 表示件数
$SleepTime = 1     # 計測間隔(秒)

# 1回目の累積CPU時間を取得
$sample1 = Get-Process | Where-Object { $_.CPU } | ForEach-Object {
    [PSCustomObject]@{
        Name = $_.ProcessName
        Id   = $_.Id
        CPU  = $_.CPU
    }
}

# 指定秒数待機
Start-Sleep -Seconds $SleepTime

# 2回目の累積CPU時間を取得
$sample2 = Get-Process | Where-Object { $_.CPU } | ForEach-Object {
    [PSCustomObject]@{
        Name = $_.ProcessName
        Id   = $_.Id
        CPU  = $_.CPU
    }
}

# プロセスごとのCPU使用時間差分を計算
$cpuDiff = foreach ($proc1 in $sample1) {
    # 同一PIDのプロセスを抽出
    $proc2 = $sample2 | Where-Object { $_.Id -eq $proc1.Id }
    
    if ($proc2) {
        $delta = $proc2.CPU - $proc1.CPU
        [PSCustomObject]@{
            Name      = $proc1.Name
            Id        = $proc1.Id
            CPU_Usage = [math]::Round($delta * 100, 2) # 秒単位の差分をパーセント近似に変換
        }
    }
}

# 結果の出力(降順ソート > 上位指定件数を抽出)
$cpuDiff | Sort-Object -Property CPU_Usage -Descending | Select-Object -First $TopCount

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトは reCAPTCHA によって保護されており、Google のプライバシーポリシー および 利用規約 に適用されます。

reCaptcha の認証期間が終了しました。ページを再読み込みしてください。

ABOUT ME
ポコぺん
ポコぺん
ぷぅ
北海道在住のITエンジニア。

システム構築および運用・保守を経験。
ESXi、Azure、Proxmox VEについて基礎的な理解あり。
C系言語、PowerShellスクリプト、Excelマクロなどを少々扱う。
記事URLをコピーしました