メモリ使用量を取得する(C#)

# メモリ使用量を取得する

C# (.NET Framework)において、メモリ使用量を取得する方法が必要になる度に調べて一向に覚えられないので、覚書として記事を書いておきます。

以下の環境で動作確認を行っています。

* .NET Framework 4.7.2


## プロセスが使用しているメモリ量

プロセス(アプリケーション)自身が使用しているメモリ使用量を取得する方法です。

### System.Environment

`System.Environment`クラスの`WorkingSet`プロパティでプロセスに割り当てられている物理メモリ量が取得できます(byte単位)。

```csharp
// 物理メモリ量 ex) 135,815,168
Console.WriteLine($"{System.Environment.WorkingSet:N0}");
```

### System.Diagnostics.Process

`System.Environment.Process`インスタンスにて、プロセスに割り当てられている物理メモリ量(`WorkingSet64`プロパティ)と仮想メモリ量(`VirtualMemorySize64`プロパティ)がそれぞれ取得できます(byte単位)。

```csharp
// Processインスタンスを取得
System.Diagnostics.Process proc = System.Diagnostics.Process.GetCurrentProcess();

// 物理メモリ量 ex) 136,282,112
Console.WriteLine($"{proc.WorkingSet64:N0}");
// 仮想メモリ量 ex) 290,201,600
Console.WriteLine($"{proc.VirtualMemorySize64:N0}");
```

一度取得した`Process`インスタンスを用いて、複数回メモリ量の取得を行いたい場合、メモリ量の取得の前に`Refresh`メソッドを実行する必要があります。[^1]

[^1]: `Refresh`メソッドを実行しない場合、前回取得した時点でのメモリ量が返されます。

```csharp
proc.Refresh();
```

### System.GC

`System.GC`クラスの`GetTotalMemory`メソッドで、マネージドメモリに割り当てられている物理メモリ量を取得できます。[^2]

[^2]: `System.GC.GetTotalMemory`メソッドが返すメモリ使用量は、多くの場合で先の2例に比べると少ない数値となります。こちらの方が実際にデータの格納で消費されているメモリ量に近いと思われます(厳密には正しい表現ではありません)。

```csharp
// 物理メモリ量 ex) 113,060,872
Console.WriteLine($"{System.GC.GetTotalMemory(false):N0}");
```

引数に`true`を指定して実行すると、ガベージコレクションを行ってからメモリ使用量を取得します。

```csharp
// ex) 8,996,152
Console.WriteLine($"{System.GC.GetTotalMemory(true):N0}");
```



## PC全体のメモリ量

PC全体のメモリ量を取得する方法です。

### WMI (Win32_OperatingSystem)

WMI (Windows Management Instrumentation) を使用してPC(OS)が管理しているメモリ使用量を取得します。 [^3]

[^3]: WMIからはメモリ使用量以外にも様々なOSが管理する情報が取得できますが、ここでは割愛します。

| プロパティ名 | 取得値
| -- | -- 
| `TotalVisibleMemorySize` | 全体物理メモリ量
| `FreePhysicalMemory` | 利用可能物理メモリ量(空きメモリ量)
| `TotalVirtualMemorySize` | 全体仮想メモリ量
| `FreeVirtualMemory` | 利用可能仮想メモリ量

以下の方法で取得したメモリ使用量は **KiB単位** となるのでご注意ください。

* プロジェクトの参照アセンブリに `System.Management` を追加する必要があります。

```csharp
// WMIインスタンスを取得
using (System.Management.ManagementClass mc = new System.Management.ManagementClass("Win32_OperatingSystem"))
using (System.Management.ManagementObjectCollection ins = mc.GetInstances())
{
    foreach (System.Management.ManagementObject mo in ins)
    {
        memory(mo); // ローカルメソッド呼び出し
        mo.Dispose();
        break;
    }
}

// メモリ使用量取得メソッド(ここではローカルメソッドとして記載)
void memory(System.Management.ManagementObject mo)
{
    // 全体物理メモリ量 ex) 16,731,488
    Console.WriteLine($"{decimal.Parse(mo["TotalVisibleMemorySize"].ToString()):N0}");
    // 利用可能物理メモリ量 ex) 4,194,176
    Console.WriteLine($"{decimal.Parse(mo["FreePhysicalMemory"].ToString()):N0}");
    // 全体仮想メモリ量 ex) 25,644,384
    Console.WriteLine($"{decimal.Parse(mo["TotalVirtualMemorySize"].ToString()):N0}");
    // 利用可能仮想メモリ量 ex) 7,106,136
    Console.WriteLine($"{decimal.Parse(mo["FreeVirtualMemory"].ToString()):N0}");
}
```

### Microsoft.VisualBasic.Devices.ComputerInfo

`Microsoft.VisualBasic.Devices.ComputerInfo`インスタンスにて、PC(OS)が管理している物理メモリの全体量(`TotalPhysicalMemory`プロパティ)と利用可能量(`AvailablePhysicalMemory`プロパティ)がそれぞれ取得できます(byte単位)。[^4]

[^4]: 仮想メモリを取得するプロパティも用意されていますが、このプロパティは実際に確保されている仮想メモリの量ではなく、プロセスがアクセス可能な仮想メモリ量を返すようです。
その為、32bit向けビルドでは約4GB、64bit向けビルドではOSのエディションにもよりますが、私の環境では128TBとなりました。

* プロジェクトの参照アセンブリに `Microsoft.VisualBasic` を追加する必要があります。

```csharp
// ComputerInfoインスタンスを取得
Microsoft.VisualBasic.Devices.ComputerInfo info = new Microsoft.VisualBasic.Devices.ComputerInfo();

// 全体物理メモリ量 ex) 17,133,043,712
Console.WriteLine($"{info.TotalPhysicalMemory,15:N0}");
// 利用可能物理メモリ量 ex) 5,095,968,768
Console.WriteLine($"{info.AvailablePhysicalMemory,15:N0}");
```