C#でハードウェア予約済みメモリを取得する方法|WindowsタスクマネージャーHardware reservedを算出

Windowsのタスク マネージャーに表示される「ハードウェア予約済み(Hardware reserved)」は、アプリから“そのまま”取得できるAPIが用意されていません。しかし「搭載RAM」と「OSが利用可能な物理メモリ」の差分として扱うと、C#から近い値を再現できます。ここではWin32 APIとWMIの2通りで、算出方法とハマりどころをまとめます。

目次

タスク マネージャーの「ハードウェア予約済み」とは

タスク マネージャーの[パフォーマンス]→[メモリ]に出てくる「ハードウェア予約済み」は、OSが物理メモリとして使えない領域(または起動時点で予約されている領域)を大まかに示す表示です。代表例は、内蔵GPU(iGPU)の共有メモリ(UMAフレームバッファ)、BIOS/UEFIやファームウェアが確保する領域、PCI/PCIeデバイスのMMIO(メモリマップドI/O)領域、各種の予約領域などです。

重要なのは、これは「空きメモリ」ではなく、そもそもOSの管理対象に入らない(または入れにくい)領域が含まれるという点です。そのため、プロセスのメモリ使用量を減らしても減りませんし、GCやキャッシュ解放では改善しません。値が大きいときは、設定やハードウェア構成の影響を疑うのが近道です。

表示/用語意味(ざっくり)よく混同するポイント
搭載メモリ(Installed RAM)物理的に装着されているRAM容量OSから見えない領域も含み得る
OSが利用可能な物理メモリ(Total Phys/Visible)OSが「物理メモリ」として管理できる総量これがタスク マネージャーの「使用中/利用可能」計算の母数になりやすい
ハードウェア予約済み(Hardware reserved)搭載量とOS可視量の差分として表現されることが多い環境により“差分”の定義が完全一致しないことがある

C#から「Hardware reserved」を直接取れない理由

タスク マネージャーは、複数の情報源(カーネルが持つメモリマップ、ファームウェア予約、ドライバの予約、場合によっては補正/丸め)を統合して表示します。一方で、開発者向けに「Hardware reserved」という単一の数値だけを返す公式APIは一般的ではありません。

そこで実用的なのが、「搭載量」と「OSが使える物理メモリ総量」の差分を取り、Hardware reserved相当として扱う方法です。完全一致を保証するものではありませんが、監視・診断・レポート用途では十分に役立ちます。

近い値を算出する基本方針

計算はシンプルです。ポイントは「搭載メモリ」をどのAPIで取るか、そして「OSが使える物理メモリ」をどの値にするかです。

要素意味候補単位
Installed物理的に搭載されている総メモリGetPhysicallyInstalledSystemMemory / Win32_ComputerSystem.TotalPhysicalMemoryKB or Bytes
OS VisibleOSが利用可能な物理メモリ総量GlobalMemoryStatusEx.ullTotalPhys / Win32_OperatingSystem.TotalVisibleMemorySizeBytes or KB
Hardware Reserved差分(Installed − OS Visible)算出Bytes(任意でMB/GB表示)

この考え方に沿って、C#では次の2パターンが実装しやすいです。

  • 方法A(推奨):Win32 API(P/Invoke)で「搭載」と「OS可視」を取り、差分を算出
  • 方法B:WMI(System.Management)で「総物理」と「可視物理」を取り、差分を算出

方法A(推奨):Win32 APIで算出する

タスク マネージャーの表示に寄せたいなら、まず試したいのがWin32 APIの組み合わせです。特にGetPhysicallyInstalledSystemMemoryは「搭載量(Installed)」に近い値が取りやすく、差分計算が安定します。

使用するAPIと役割

API取得できるもの返却単位用途
GetPhysicallyInstalledSystemMemory搭載されている物理メモリ量KBInstalled RAM相当
GlobalMemoryStatusExメモリ統計(物理/仮想/ページファイルなど)BytesullTotalPhysをOS可視量として使用

サンプルコード(Win32 APIでHardware reserved相当を算出)

以下は、搭載量(KB)とOS可視量(Bytes)を取り、差分をBytesで返す実装例です。タスク マネージャーと同様にMB/GB表記にしたい場合は、最後に単位変換して表示します。

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

public static class HardwareReservedMemory
{
    public readonly struct Result
    {
        public Result(ulong installedBytes, ulong osVisibleBytes, ulong reservedBytes)
        {
            InstalledBytes = installedBytes;
            OsVisibleBytes = osVisibleBytes;
            ReservedBytes = reservedBytes;
        }

        public ulong InstalledBytes { get; }
        public ulong OsVisibleBytes { get; }
        public ulong ReservedBytes { get; }
    }

    // 搭載メモリ(Installed RAM)をKBで取得
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GetPhysicallyInstalledSystemMemory(out ulong totalMemoryInKilobytes);

    // OSが利用可能な物理メモリ(Total Phys)を含む統計を取得
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct MEMORYSTATUSEX
    {
        public uint dwLength;
        public uint dwMemoryLoad;
        public ulong ullTotalPhys;
        public ulong ullAvailPhys;
        public ulong ullTotalPageFile;
        public ulong ullAvailPageFile;
        public ulong ullTotalVirtual;
        public ulong ullAvailVirtual;
        public ulong ullAvailExtendedVirtual;
    }

    public static Result Get()
    {
        if (!GetPhysicallyInstalledSystemMemory(out ulong installedKb) || installedKb == 0)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error(),
                "GetPhysicallyInstalledSystemMemory failed.");
        }

        var mem = new MEMORYSTATUSEX
        {
            dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX))
        };

        if (!GlobalMemoryStatusEx(ref mem))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error(),
                "GlobalMemoryStatusEx failed.");
        }

        ulong installedBytes = installedKb * 1024UL;
        ulong osVisibleBytes = mem.ullTotalPhys;

        // 差分が負になる環境(丸め/実装差)に備えて0下限にする
        ulong reservedBytes = installedBytes > osVisibleBytes
            ? installedBytes - osVisibleBytes
            : 0UL;

        return new Result(installedBytes, osVisibleBytes, reservedBytes);
    }

    public static string FormatBytes(ulong bytes)
    {
        const double KiB = 1024d;
        const double MiB = KiB * 1024d;
        const double GiB = MiB * 1024d;

        if (bytes >= (ulong)GiB) return (bytes / GiB).ToString("0.##") + " GB";
        if (bytes >= (ulong)MiB) return (bytes / MiB).ToString("0.##") + " MB";
        if (bytes >= (ulong)KiB) return (bytes / KiB).ToString("0.##") + " KB";
        return bytes + " B";
    }
}

// 使い方例
// var r = HardwareReservedMemory.Get();
// Console.WriteLine("Installed : " + HardwareReservedMemory.FormatBytes(r.InstalledBytes));
// Console.WriteLine("OS Visible : " + HardwareReservedMemory.FormatBytes(r.OsVisibleBytes));
// Console.WriteLine("Reserved  : " + HardwareReservedMemory.FormatBytes(r.ReservedBytes));

計算結果をタスク マネージャーに寄せるコツ

  • x64で実行する:32bitプロセスでもAPI自体は呼べますが、周辺の扱い(型、丸め、外部ライブラリ)で不利になりやすいので、基本はx64を推奨します。
  • 単位変換を揃える:タスク マネージャーの表記はGB/MBと表示されますが、内部的には2進(1024系)で丸めていることがあります。比較するときはMB(MiB)単位に落として比べると差が見えやすいです。
  • 差分が小さくズレるのは正常範囲:数十MB程度の差は、予約領域の扱い方や表示の丸めで起きます。監視では「桁が違うズレ」かどうかに注目すると判断が早いです。
比較のしかたおすすめ理由
そのままBytesで比較△表示の丸め差が目立ちやすい
MB(1024×1024)に変換して比較◎タスク マネージャーの見た目と近い粒度
GB(1024×1024×1024)に変換して比較○大きな差分の有無を素早く確認できる

方法B:WMI(System.Management)で算出する

Win32 APIが使いにくい(ポリシー上P/Invokeを避けたい、既存資産がWMI中心、リモートの情報も取りたい)場合は、WMIでの算出が現実的です。WMIは値の取り回しが楽な反面、速度・依存関係・例外の出方が異なるため、用途に合わせて選びます。

利用するWMIクラスとプロパティ

WMIクラスプロパティ意味単位
Win32_ComputerSystemTotalPhysicalMemory総物理メモリ(システム全体)Bytes
Win32_OperatingSystemTotalVisibleMemorySizeOSから見える物理メモリ総量KB

サンプルコード(WMIでHardware reserved相当を算出)

.NET Frameworkでは標準で利用できることが多く、.NET(Core/5+/6+/8+)系ではWindows向けにSystem.Managementを参照する構成が一般的です。実行環境がWindowsであることを前提にしてください。

using System;
using System.Management;

public static class HardwareReservedMemoryWmi
{
    public readonly struct Result
    {
        public Result(ulong totalPhysicalBytes, ulong osVisibleBytes, ulong reservedBytes)
        {
            TotalPhysicalBytes = totalPhysicalBytes;
            OsVisibleBytes = osVisibleBytes;
            ReservedBytes = reservedBytes;
        }

        public ulong TotalPhysicalBytes { get; }
        public ulong OsVisibleBytes { get; }
        public ulong ReservedBytes { get; }
    }

    public static Result Get()
    {
        ulong totalPhysicalBytes = 0;
        ulong totalVisibleKb = 0;

        using (var cs = new ManagementObjectSearcher(
            "SELECT TotalPhysicalMemory FROM Win32_ComputerSystem"))
        {
            foreach (ManagementObject mo in cs.Get())
            {
                totalPhysicalBytes = (ulong)(mo["TotalPhysicalMemory"] ?? 0UL);
                break;
            }
        }

        using (var os = new ManagementObjectSearcher(
            "SELECT TotalVisibleMemorySize FROM Win32_OperatingSystem"))
        {
            foreach (ManagementObject mo in os.Get())
            {
                totalVisibleKb = (ulong)(mo["TotalVisibleMemorySize"] ?? 0UL);
                break;
            }
        }

        ulong osVisibleBytes = totalVisibleKb * 1024UL;

        ulong reservedBytes = totalPhysicalBytes > osVisibleBytes
            ? totalPhysicalBytes - osVisibleBytes
            : 0UL;

        return new Result(totalPhysicalBytes, osVisibleBytes, reservedBytes);
    }
}

WMIで算出するときの注意点

  • 取得が遅いことがある:WMIはCOMやサービス経由で情報を集めるため、短周期ポーリングには不向きなことがあります。監視用途なら取得間隔を長めにする、キャッシュする、必要なときだけ取得するなどの工夫が有効です。
  • 例外処理とリソース解放:WMIは環境差が出やすいので、タイムアウトや例外を想定し、usingで確実に破棄するのが安全です。
  • 「搭載量」と完全一致しないことがある:Win32_ComputerSystem.TotalPhysicalMemoryが、BIOS表示のInstalled RAMと微妙にズレる機種もあります。タスク マネージャーに寄せたいなら方法Aを先に試すのが無難です。

方法Aと方法Bの比較

観点方法A(Win32 API)方法B(WMI)
タスク マネージャーへの近さ◎(一致しやすい)○(環境差が出ることがある)
実装の手軽さ○(P/Invokeが必要)◎(プロパティ取得で完結)
実行速度◎(高速)△(遅いことがある)
依存関係Windows API(kernel32)System.Management / WMIサービス
リモート取得△(別途仕組みが必要)○(WMIの仕組み次第で可能)

「一致しない」「異常に大きい」ときに疑うポイント

差分が数百MB〜数GBと大きい場合は、アプリの問題というよりハードウェア構成やファームウェア設定の影響が濃厚です。以下は現場で遭遇しやすい原因とチェック観点です。

原因の例起きやすい状況目安の増え方確認・対処の方向性
内蔵GPU(UMA/共有メモリ)ノートPC/省電力CPU、iGPU有効数百MB〜数GBBIOS/UEFIのUMA Frame Buffer設定、GPUメモリ割当の見直し
PCIeデバイスのMMIO領域高機能GPU、複数デバイス、周辺機器が多い数十MB〜数百MB構成変更で増減するか、デバイス構成(GPU/拡張カード)を確認
メモリリマップ関連の設定古いPC/特殊なBIOS設定GB単位で欠けることもMemory Remap機能の有無や設定を確認(機種依存)
仮想化/ハイパーバイザHyper-V有効、仮想マシン、VBSなど環境により変動ホスト/ゲストのどちらで見ているかを整理し、要件に合わせて計測
ファームウェア/ドライバ予約特定機種、特定ドライバ更新後数十MB〜BIOS更新・ドライバ更新の影響を切り分け、メーカー情報も参照

なお、業務で「ハードウェア予約済みが増えた=メモリリーク」と誤解されることがあります。差分算出は起動構成に依存する固定成分を見ているので、アプリの挙動と切り分けるためにも、同時に「使用中」「コミット」「利用可能」などの指標も併記すると説明がスムーズです。

実運用で役立つ表示・ログの工夫

Hardware reserved相当を取得できたら、サポートや監視で使いやすい形に整えます。特に有用なのは同じ単位・同じ丸めで揃えてログに残すことです。

  • Bytesのまま保持:計算や比較の基準はBytesに統一する(後で自由に表示変更できる)
  • 表示はMB(1024×1024)で丸める:タスク マネージャーと見比べやすい
  • Installed / OS Visible / Reservedをセットで出す:差分だけだと原因調査が進まない
ログ項目例例意図
InstalledBytes34359738368搭載量の生値
OsVisibleBytes33639288832OS可視量の生値
ReservedBytes720449536Hardware reserved相当(差分)
ReservedMB687運用で見やすい単位に丸めた値

また、同一端末での比較だけでなく、複数端末の棚卸しにも使うなら「CPU型番」「GPU構成」「メモリ構成(枚数/容量)」「BIOSバージョン」なども合わせて記録しておくと、後から差分が説明しやすくなります。

よくある質問(つまずきポイントの整理)

Reservedが0になる/マイナスになりそう

差分が0になるのは、搭載量として取得した値とOS可視量がほぼ同じ(あるいは搭載量の取得に失敗して0になっている)可能性があります。コードでは、丸めや取得元の差で負になるケースに備え、0下限にするのが安全です。まずは「Installed」と「OS Visible」をそれぞれログに出して、どちらが期待値と違うかを確認してください。

WMIの値とWin32 APIの値が合わない

WMIのTotalPhysicalMemoryは機種依存でズレることがあり、TotalVisibleMemorySizeもOS側の見え方に影響されます。タスク マネージャーに寄せる目的ならWin32 APIを優先し、WMIは「P/Invokeが難しい環境の代替」「リモート取得の選択肢」として割り切ると設計がブレません。

値が大きすぎて不安

数GB単位のHardware reservedは珍しくありません。特に内蔵GPUの割当が大きい構成や、特殊なメモリマップ(デバイスやBIOS設定)では増えます。アプリ対策ではなく、構成・設定・更新履歴(BIOS/ドライバ/Windows更新)を含めて切り分けるのが近道です。

まとめ

タスク マネージャーの「ハードウェア予約済み(Hardware reserved)」は、C#から単一APIで直接読むよりも、「搭載メモリ」と「OSが利用可能な物理メモリ」の差分として算出するのが実用的です。タスク マネージャーに近い値を狙うなら、GetPhysicallyInstalledSystemMemoryとGlobalMemoryStatusExを組み合わせた方法Aをまず試し、要件に応じてWMIの方法Bを使い分けると、開発・運用の両方で扱いやすくなります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次