はじめに:メモリ使用量取得は「どれくらい“盛っている”かを数字で知るためのもの」
パフォーマンスの話をしていると、必ず出てくるのが「メモリ食いすぎてない?」という心配です。
でも、感覚だけで「重そう」と言っていても、何も改善できません。
そこで必要になるのが メモリ使用量取得、つまり「今このプロセスがどれくらいメモリを使っているか」を数字で知ることです。
ここでは、初心者向けに
GC.GetTotalMemory で「マネージドヒープの使用量」を見るProcess クラスで「プロセス全体のメモリ」を見る
ログと組み合わせた「メモリ使用量計測ユーティリティ」
どんな場面で使うと意味があるか
を、例題付きでかみ砕いて説明します。
GC.GetTotalMemory で「.NET が管理しているメモリ量」を見る
マネージドヒープのサイズをざっくり知る
C#(.NET)の世界では、new したオブジェクトは「マネージドヒープ」という領域に置かれ、
ガベージコレクタ(GC)が不要になったタイミングで片付けてくれます。
この「マネージドヒープが今どれくらいのサイズか」を教えてくれるのが GC.GetTotalMemory です。
public static void ShowManagedMemory()
{
// 第1引数は「すぐに GC を走らせてから測るかどうか」
// true にすると、できるだけ最新の状態に近づけてから測る
long bytes = GC.GetTotalMemory(forceFullCollection: true);
Console.WriteLine($"マネージドヒープ使用量: {bytes} バイト");
Console.WriteLine($"約 {bytes / 1024.0 / 1024.0:F2} MB");
}
C#forceFullCollection: true にすると、GC が一度走って「いらないオブジェクト」を片付けてから測ってくれます。
そのため、「今本当に必要なオブジェクトがどれくらいメモリを使っているか」に近い値になります。
ここでの重要ポイントは、「これは“マネージドヒープのサイズ”であって、“プロセス全体のメモリ”ではない」ということです。
ネイティブライブラリや、JIT コンパイルされたコード、スタックなどは含まれていません。
Process クラスで「プロセス全体のメモリ」を見る
実際に OS から見える「このアプリのメモリ使用量」
「タスクマネージャーに出ているような数字」を知りたい場合は、System.Diagnostics.Process を使います。
using System.Diagnostics;
public static void ShowProcessMemory()
{
using var process = Process.GetCurrentProcess();
long workingSet = process.WorkingSet64; // 実メモリ(物理メモリ上に載っている量)
long privateBytes = process.PrivateMemorySize64; // このプロセス専用の仮想メモリ
Console.WriteLine($"WorkingSet: {workingSet} バイト(約 {workingSet / 1024.0 / 1024.0:F2} MB)");
Console.WriteLine($"PrivateBytes: {privateBytes} バイト(約 {privateBytes / 1024.0 / 1024.0:F2} MB)");
}
C#ざっくり言うと、
WorkingSet
今このプロセスが物理メモリ上に展開している量
PrivateBytes
このプロセス専用に確保している仮想メモリ量
というイメージです。
ここでの重要ポイントは、「プロセス全体のメモリは、.NET 以外の要素も含んでいる」ということです。
ネイティブライブラリ、JIT コード、スタック、OS の管理領域なども含まれるため、
「GC.GetTotalMemory より大きい数字になる」のが普通です。
メモリ使用量をログに残すユーティリティを作る
「どのタイミングでどれくらい使っているか」を後から追えるようにする
コンソールに出すだけだと、その場限りで終わってしまいます。
業務システムでは、「特定の処理の前後でメモリ使用量をログに残しておく」と、
後から「この処理のときだけ急にメモリが増えている」といった分析ができるようになります。
ILogger と組み合わせたユーティリティの例です。
using System.Diagnostics;
using Microsoft.Extensions.Logging;
public static class MemoryLogger
{
public static void LogCurrentMemory(ILogger logger, string context)
{
using var process = Process.GetCurrentProcess();
long managedBytes = GC.GetTotalMemory(forceFullCollection: false);
long workingSet = process.WorkingSet64;
long privateBytes = process.PrivateMemorySize64;
logger.LogInformation(
"MemoryUsage Context={Context} Managed={ManagedMB:F2}MB WorkingSet={WorkingSetMB:F2}MB PrivateBytes={PrivateMB:F2}MB",
context,
managedBytes / 1024.0 / 1024.0,
workingSet / 1024.0 / 1024.0,
privateBytes / 1024.0 / 1024.0);
}
}
C#使い方の例です。
MemoryLogger.LogCurrentMemory(_logger, "ユーザー一覧取得前");
// ユーザー一覧取得処理
var users = await LoadUsersAsync();
MemoryLogger.LogCurrentMemory(_logger, "ユーザー一覧取得後");
C#ログには、例えばこんな行が残ります。
「MemoryUsage Context=ユーザー一覧取得前 Managed=50.23MB WorkingSet=120.45MB PrivateBytes=130.10MB」
「MemoryUsage Context=ユーザー一覧取得後 Managed=80.12MB WorkingSet=150.78MB PrivateBytes=160.30MB」
ここでの重要ポイントは、「メモリ使用量を“文脈(どの処理か)付きで”残す」ことです。
単に数字だけを残しても意味がなく、「どの処理の前後でどう変化したか」が分かるようにしておくことが大事です。
実務での使いどころ:メモリリークや“重い画面”の特定
「どこで増えて、どこで減っていないか」を見る
メモリ使用量取得が本当に役に立つのは、次のような場面です。
特定の画面を何度も開くと、だんだん重くなっていく
長時間動かしていると、メモリ使用量が右肩上がりで増え続ける
バッチ処理の途中で急にメモリが跳ね上がる
こういうときに、「処理の前後で MemoryLogger を呼んでおく」と、
「この画面を開くたびに Managed メモリが 50MB ずつ増えている」
「このバッチのこのステップでだけ WorkingSet が急増している」
といったことが分かるようになります。
そこから、
使い終わったオブジェクトを保持し続けていないか
キャッシュが増え続けていないか
画像や大きな配列を解放していないか
といった観点でコードを見直していくことができます。
ここでの重要ポイントは、「メモリ使用量取得は“原因そのもの”を教えてくれるわけではなく、“怪しい場所を絞り込むためのヒント”になる」ということです。
数字を見て、「この辺が怪しい」と当たりを付けてから、コードやプロファイラで深掘りしていく流れになります。
まとめ:メモリ使用量取得は“重さを数字で見える化する”ためのユーティリティ
メモリ使用量取得の本質を一言で言うと、
「このアプリが今どれくらいメモリを使っているかを、
感覚ではなく、バイト数・MB という数字で把握し、
どこで増えてどこで減っていないかを追えるようにする」
ことです。
押さえておきたいポイントを整理すると、
GC.GetTotalMemory で「マネージドヒープの使用量」をざっくり見られる。Process.GetCurrentProcess() から WorkingSet64 や PrivateMemorySize64 を取ると、「プロセス全体のメモリ」を見られる。
ILogger と組み合わせた MemoryLogger を作って、「処理の前後でメモリ使用量をログに残す」と、後から分析しやすい。
メモリ使用量取得は、メモリリークや“重い画面・重い処理”の特定に役立つが、原因そのものではなく“ヒント”として使う。
ここまでイメージできていれば、「なんかメモリ食ってそう」から一歩進んで、
“数字で語れるメモリの健康診断”に踏み出せるようになります。
