Windowsの「メモリ使用量」は何を表しているのか ── Working Set・Private Bytes・Commit・ページファイルを正しく読む

· · Windows, Windows開発, メモリ管理, Working Set, Private Bytes, Commit, ページファイル, パフォーマンス監視, 障害調査, Sysinternals

タスクマネージャーでは、あるプロセスの「メモリ」が1.2GBと表示されている。ところがProcess ExplorerではWorking Setが1.5GB、Private Bytesが2.4GB、VMMapではSizeがさらに大きい。システム全体を見ると「コミット済み 19.6/31.8GB」と出ている。

では、このアプリは結局、何GBのメモリを使っているのでしょうか。

答えは、何を知りたいかによって、見るべき数字が変わるということです。現在RAMに載っている量を知りたいのか、そのプロセス固有の割り当て量を知りたいのか、システムが将来も支えると約束した量を知りたいのか、単に仮想アドレスを確保している範囲を知りたいのかで、見る指標は異なります。

Windowsのメモリ指標が難しいのは、どれも「メモリ」という同じ言葉で表示されるのに、実際には次の別々の軸を測っているからです。

  • アドレス空間をどれだけ使っているか
  • コミットをどれだけ消費しているか
  • 現在物理RAMに常駐しているか
  • そのページがプロセス固有か、共有可能か
  • システム全体で、あとどれだけ割り当てを支えられるか

この記事では、Windows 10/11および現行のWindows Serverでアプリのメモリ増加やシステム全体のメモリ不足を調べる人を対象に、Working Set、Private Working Set、Private Bytes、Commit、Virtual Bytes、ページファイル、Available、ページフォールトの関係を一枚につなぎます。

.NETのオブジェクトが回収されない原因を追う手順は「.NETでGC待ちとメモリリークを見分ける」、VMMapやProcess Explorerの具体的な操作は「Process Explorer / Handle / VMMap実践」で詳しく扱っています。本記事は、その前提になるWindows OS側の数字の読み方に集中します。

1. まず結論

  • Working Setは、現在RAMに常駐しているページです。プロセス固有のページだけでなく、DLLのコードやメモリマップトファイルなど、他プロセスと共有できるページも含みます。1
  • Private Working Setは、Working Setのうち現在そのプロセスだけに属する部分です。「今このプロセスが固有に占有しているRAM」を見る近似として使えますが、アプリが確保した全量ではありません。2
  • Private Bytesは、そのプロセス固有のコミット量です。現在RAMに載っているかどうかとは別の指標です。Win32 APIの構造体にあるPagefileUsageも、現在のWindowsでは実質的に同じCommit Chargeを表し、ページファイルへ実際に書かれたバイト数ではありません。2
  • タスクマネージャーの「コミット済み X/Y」は、Xがシステム全体の現在のコミット量、Yがコミット上限です。Xはページファイルの使用量ではありません。Yは概ねRAMとページファイルの合計で決まります。3
  • ReserveとCommitは違います。仮想アドレスをReserveしただけなら、その範囲を将来使うために取り置いただけで、RAMもコミット上限も同じ量だけ消費しません。45
  • ページフォールトは、必ずしもディスクI/Oではありません。RAM内で解決できるソフトフォールトと、ページファイル・実行ファイル・メモリマップトファイルなどから読むハードフォールトがあります。16
  • メモリリークは1回の値ではなく、同じ負荷を繰り返したときの傾きで判断します。特にPrivate Bytesや内訳が、処理終了後も段階的に増え続け、同じ定常状態へ戻らないかを見ます。

一文でまとめるなら、Working Setは「今RAMにある量」、Private Bytesは「このプロセス固有に約束された量」、Commitは「システム全体が約束した量」です。

Windowsの主要なメモリ指標を選ぶ知りたい対象がRAM常駐量、プロセス固有のコミット量、システム全体のコミット量、仮想アドレス範囲のどれかによって見る指標が変わる今RAMにある量プロセス固有の約束量システム全体の約束量確保したアドレス範囲「メモリ使用量」で何を知りたいかWorking SetPrivate BytesSystem CommitVirtual Bytes / Reserved物理RAMへの常駐プロセス固有のCommitCommit Limitと比較仮想アドレス空間

図1: 「メモリが多い」という観察を、最初に4種類の問いへ分解する。

2. 「メモリ使用量」を4つの軸に分ける

最初に、Windowsのメモリを「1本の棒」ではなく、4つの軸で考えます。

1ページを分類する4つの独立した軸仮想アドレスの状態、コミット済みページの裏付け、物理RAMへの常駐、他プロセスとの共有可能性を別々に確認する1ページを4つの軸で見るアドレス状態Free / Reserved / Committed裏付けPage-file-backed / File-backedRAM常駐Resident / Not resident共有可能性Private / Shareable

図2: 1つのページでも、アドレス状態・裏付け・常駐・共有性は別々に決まる。

MappedはFree・Reserved・Committedと並ぶアドレス状態ではなく、領域の種類です。マップトビューのページもCommittedになり得ます。また、Privateは裏付け媒体ではなく共有可能性の分類です。そのため、裏付けはPage-file-backedかFile-backedか、共有可能性はPrivateかShareableかを別々に読みます。

この4軸を組み合わせると、代表的な指標の関係は次のようになります。

ページの状態 Working Set Private Working Set Private Bytes Virtual Bytes系
プロセス固有・コミット済み・RAM常駐 含む 含む 含む 含む
プロセス固有・コミット済み・RAM非常駐 含まない 含まない 含む 含む
DLLやマップトファイルの共有ページ・RAM常駐 含む 原則含まない 原則含まない 含む
予約済みだが未コミット 含まない 含まない 含まない 含み得る
未使用のアドレス範囲 含まない 含まない 含まない 通常含まない
ページの種類と主要メモリ指標の対応Privateで常駐するページ、Privateで非常駐のページ、共有で常駐するページ、予約のみの範囲がどの指標へ含まれるかを示すPrivate・Commit済み・RAM常駐Private・Commit済み・RAM非常駐共有ページ・RAM常駐Reserved・未CommitWorking SetPrivate Working SetPrivate BytesVirtual Bytes系

図3: Working SetとPrivate Bytesは異なるページ集合を数えるため、単純な包含関係にならない。

ここで重要なのは、Working SetとPrivate Bytesが単純な包含関係ではないことです。

Private Bytesには、プロセス固有だが今はRAMに常駐していないページが含まれます。一方のWorking Setには、DLLのコードや共有メモリなど、Private Bytesには数えない共有ページが含まれます。そのため、プロセスや時点によってWorking SetがPrivate Bytesより大きいことも、その逆もあります。

また、複数プロセスのWorking Setを単純に合計すると、共有DLLなどの同じ物理ページを複数回数える可能性があります。「各プロセスのWorking Set合計=使用中RAM」とは限りません。

3. 仮想アドレス空間 ── ReserveとCommitは別物

3.1. 仮想アドレスは物理RAMの番地ではない

各プロセスは、自分専用の仮想アドレス空間を持ちます。アプリが扱うポインターは物理RAMの位置を直接示しているわけではなく、Windowsがページテーブルを使って仮想アドレスを物理ページやファイル上のデータへ対応付けます。7

このため、64GBのRAMを搭載したPCでも、ある32bitプロセスが使える仮想アドレス空間は通常それよりはるかに小さい、ということが起こります。逆に、物理RAMより大きな仮想アドレス空間を持つ64bitプロセスも普通です。

3.2. Reservedは「住所を押さえた」だけ

VirtualAllocMEM_RESERVEは、将来使うために連続した仮想アドレス範囲を確保します。この段階ではページへ物理ストレージは関連付けられず、その範囲を読み書きすることもできません。45

たとえば、データベースやランタイムが将来の成長用に8GBのアドレス範囲をReserveしていても、それだけで8GBのRAMや8GBのPrivate Bytesを消費したことにはなりません。

3.3. Committedは「必要になったとき支える」と約束した状態

MEM_COMMITは、その仮想ページをCommitted状態にし、Windowsが必要な裏付けを提供すると約束する操作です。実際に読み取り・書き込み・実行が許可されるかは、PAGE_READONLYPAGE_READWRITEPAGE_EXECUTEPAGE_NOACCESSなどのページ保護で別に決まり、Commit済みであること自体は「読み書き可能」を意味しません。コミットした時点でシステムのCommit Chargeへ計上されますが、実際の物理ページは初回アクセス時まで割り当てられない場合があります。初めて触れたページはゼロで初期化され、Demand-zero faultを経てWorking Setへ入ります。51

したがって、同じ「確保した」でも次の3段階があります。

ReserveからCommitとRAM常駐までの3段階仮想アドレスを予約し、ページをコミットし、初回アクセスで物理ページが割り当てられてWorking Setへ入る流れを示すMEM_COMMIT初回アクセス・Demand-zero fault未アクセスならMEM_RESERVE: アドレス範囲を確保Virtual Bytes系に反映Commit済み: ページ保護に応じてアクセス可能Private Bytes / System Commitに反映物理ページを割り当ててRAM常駐Working Setに反映Commit済みだが非常駐

図4: Reserve、Commit、初回アクセスは別の出来事で、それぞれ別の指標を動かす。

この3段階が、それぞれVirtual Bytes系、Private Bytes、Working Setの数字を別々に動かします。

3.4. 空きRAMがあってもOutOfMemoryになる理由

メモリ割り当ての成否は、空きRAMだけでは決まりません。

  • プロセスの仮想アドレス空間を使い切った
  • 必要な大きさの連続した空きアドレス範囲がない
  • システム全体のCommit ChargeがCommit Limitへ達した
  • Job Object、コンテナ、ランタイム、ライブラリに独自の上限がある
  • 32bitプロセスである
  • ネイティブヒープが断片化している

64bit Windows上でも、32bitプロセスのユーザーモード仮想アドレス空間は、IMAGE_FILE_LARGE_ADDRESS_AWAREが無効なら通常2GBです。有効な32bitアプリは64bit Windows上で最大4GBを利用できます。8

そのため「PCには空きRAMが20GBあるのに、32bitアプリが1.6GB付近で失敗する」は矛盾ではありません。RAMの問題ではなく、アドレス空間の断片化や上限に当たっている可能性があります。

4. Working Set ── 今RAMに載っているページ

Working Setは、プロセスの仮想アドレス空間のうち、現在物理RAMに常駐しているページの集合です。1

ここには次が混在します。

  • プロセス固有のヒープやスタック
  • EXEとDLLのコード・読み取り専用データ
  • メモリマップトファイル
  • 共有メモリ
  • Copy-on-write後にそのプロセス固有になったページ
  • ランタイムや各種ライブラリが触れたページ

4.1. Working Setが増えても、割り当てが増えたとは限らない

すでにコミット済みだったページへ初めてアクセスすれば、Private Bytesが変わらないままWorking Setだけ増えることがあります。大きなファイルをメモリマップして順番に読んだ場合も、ファイル由来のページがWorking Setへ入り、Private Bytesはほとんど増えないことがあります。

逆に、Windowsがメモリ圧迫に応じてWorking SetをTrimすると、アプリが論理的には同じメモリを保持したままWorking Setだけ減ります。後で再び触れれば、ページフォールトを経て戻ります。

したがって、Working Setの下降は「アプリが解放した」、上昇は「アプリが新しく確保した」とは限りません。

Working Setだけが増減する典型的な流れ同じCommit済みページが初回アクセスでRAMへ入り、Trimで非常駐になり、再アクセスで戻る間もPrivate Bytesは計上され続ける初回アクセスメモリ圧迫でTrim再アクセスでPage Fault同じCommit済みページRAM常駐非常駐Working Setに含むWorking Setに含まないCommit中はPrivate Bytesへ計上

図5: Working Setは常駐状態に応じて上下するが、同じページのCommitが残る限りPrivate Bytesは減らない。

4.2. Working Setには共有ページが含まれる

同じDLLのコードページを10プロセスが共有している場合、そのページは各プロセスのWorking Setに見え得ますが、物理RAM上には1組しか存在しないことがあります。Working Setの合計が搭載RAMを超えても、直ちに異常ではありません。

「今このプロセスだけが固有に占有しているRAM」に近づけたい場合は、Private Working Setを見ます。ただし、これも「そのプロセスが確保した全メモリ」ではなく、あくまで現在常駐中のPrivateなページです。

4.3. Working Setを強制的に小さくしてもリークは直らない

EmptyWorkingSetSetProcessWorkingSetSizeを使うと、プロセスのWorking Setからページを追い出せます。しかし、これはコミットやヒープ上の参照を解放する操作ではありません。Private Bytesが変わらないまま見かけのRAM使用量だけ下がり、次のアクセスでページフォールトが増えることがあります。9

メモリ削減ボタンを押した直後だけタスクマネージャーの数字が小さくなり、操作を再開するとすぐ元へ戻るなら、「解放」ではなくWorking SetをTrimしただけかもしれません。

5. Private Bytes ── プロセス固有のコミット量

Private Bytesは、そのプロセス専用にコミットされた仮想メモリの量です。別プロセスと共有できないCommit Chargeを表し、現在RAMへ常駐しているかどうかは問いません。MicrosoftのPROCESS_MEMORY_COUNTERS_EXではPrivateUsageがこの値に対応します。102

Win32 APIにはPagefileUsageという紛らわしいフィールド名もありますが、現行ドキュメントはこれを「そのプロセスのCommit Charge」と定義し、PrivateUsageと同じ値だと説明しています。つまり、Private Bytes 2GBは「pagefile.sysへ2GB書かれている」という意味ではありません。2

Private Bytesには、代表的には次が影響します。

  • HeapAllocmallocnewなどが使うネイティブヒープのコミット
  • VirtualAllocで直接コミットしたPrivate Data
  • .NET GCヒープのコミット済み領域
  • スレッドスタックのうち実際にコミットされた部分
  • Copy-on-writeビュー(FILE_MAP_COPY)のマッピング時に確保される、ビュー全体分のCommit Charge
  • ライブラリやデバイスSDKが内部で保持するPrivateなバッファ

FILE_MAP_COPYで作るCopy-on-writeビューでは、各ページが将来Private化し得るため、Windowsはマッピング時点でビュー全体をページファイルで裏付けられるようCommit Chargeを確保します。そのため、実際に書き込んでPrivateコピーが作られる前でも、System CommitとプロセスのCommit Charge(Private Bytes)がビュー全体分増え得ます。11

5.1. freeやGC後もPrivate Bytesが下がらない理由

アプリケーションから見てメモリを「解放」しても、ランタイムやヒープアロケーターがその領域をOSへDecommitせず、将来の再利用用に保持することがあります。この場合、アプリ内部では再利用可能でもPrivate Bytesは高いままです。

また、大きな領域の一部だけが生き残っている、断片化している、キャッシュやプールが上限まで温まった、といった理由でも高止まりします。

したがって、Private Bytesが高いことだけではリークを証明できません。見るべきは、

  1. 同じ処理を同じ回数だけ繰り返す
  2. 処理後に同じ待機時間を置く
  3. Private Bytesが同じ水準へ戻るか、一定値で頭打ちになるか
  4. VMMapやヒープダンプで、どの領域・型が増えたかを確認する

という時間差比較です。

freeやGCの後もPrivate Bytesが下がらない理由アプリが不要にした領域をアロケーターがOSへ返す場合と再利用用に保持する場合でPrivate Bytesの変化が異なるDecommit / Release再利用用に保持アプリがfree / GCで不要にするアロケーターはOSへ返すかCommit Chargeが減るPrivate Bytesが下がる領域はCommit済みのままPrivate Bytesは高止まりプール・キャッシュ・断片化

図6: アプリ内で再利用可能になったことと、OSへCommitを返したことは同じではない。

5.2. リーク候補として強い形

次のように、負荷のたびに底が切り上がる「階段状」の増加は要注意です。

Private Bytes
  ^
  |                    ________
  |             ______|
  |      ______|
  |_____|
  +----------------------------> 同じ処理の繰り返し

ただし、階段状でも初回JIT、フォント、画像デコーダー、接続プール、キャッシュのウォームアップで数回だけ増え、その後は安定することがあります。増加していることより、定常状態へ収束しないことが重要です。

6. System Commit ── 「コミット済み X/Y」の正体

タスクマネージャーの[パフォーマンス]→[メモリ]にある「コミット済み X/Y」は、システム全体の指標です。

  • X: System Commit Charge
    現在、Windowsがシステム全体で支えると約束しているコミット済みメモリ
  • Y: System Commit Limit
    システムが支えられるコミット量の上限

Commit Limitは概ね、物理RAMとすべてのページファイルの合計で決まります。ページファイルがない場合は、搭載RAMより少し小さくなります。36

System Commit ChargeとCommit Limitの関係プロセス固有、共有セクション、カーネルのコミットが現在値Xを作り、物理RAMとページファイルが上限Yを支えるXはYを超えられない各プロセスのPrivate CommitSystem Commit Charge: Xページファイルで裏付ける共有セクションのCommitカーネルのCommit物理RAMSystem Commit Limit: Yページファイル

図7: Xは現在の約束量、Yはその約束を支えられる上限であり、ページファイル使用量の表示ではない。

System Commit Chargeには、各プロセスのPrivate Bytesの合計だけでなく、ページファイルで裏付けられる共有セクションのCommitや、カーネルが消費するCommitも含まれます。そのため、プロセス別Private Bytesの合計だけではXを完全には説明できません。

6.1. Commit Chargeはページファイル使用量ではない

RAM 16GB、ページファイル16GB、Committedが20/31GBのシステムを考えます。

この20GBは「ページファイルへ20GB書き込んだ」という意味ではありません。20GBは、Privateな変更可能ページなどについて、必要になったときWindowsがRAMまたはページファイル等の裏付けを用意すると約束している総量です。

その時点で、

  • 多くがRAMに常駐している
  • 一部がページファイルへ退避している
  • Commit済みだがまだ初回アクセスされていない
  • カーネル側のコミットとして消費されている

という状態が混在します。

ページファイルの実使用率を見たいなら、Commitとは別にPaging File(*)\% Usageを確認します。ただしMicrosoftの資料でも、ページファイル使用率が高いことだけでは性能問題とは限らず、Commit Limit到達やModified Page List、実際のページングI/Oと合わせて判断するよう説明されています。6

6.2. Commit Limitへ近づくと何が起きるか

System Commit ChargeがCommit Limitへ達すると、新しいコミット要求を支えられません。プロセスのメモリ割り当て失敗、アプリのクラッシュ、操作不能などにつながります。3

ここでは「空きRAM」よりCommitのX/Yが重要です。Working SetをTrimして空きRAMを作っても、Commit Chargeそのものが減らなければ、Commit Limit到達は解消しません。

6.3. ページファイルの3つの役割

ページファイルには主に次の役割があります。

  1. Commit Limitを拡張する
  2. 使用頻度の低い変更済みページをRAMから退避できるようにする
  3. 構成に応じてシステムクラッシュダンプを支える

ページファイルを無効化すると、ディスクI/Oが必ず減って速くなる、という単純な関係ではありません。むしろCommit Limitを下げ、変更済みだが当面使わないページをRAMへ残しやすくし、クラッシュ時に必要なダンプを採取できなくする可能性があります。36

ページファイルの適切なサイズは、搭載RAMだけでは決まりません。Microsoftも、ピーク時のSystem Commit Chargeと必要なクラッシュダンプの種類がシステムごとに異なるため、一般化できないと説明しています。6

7. 物理RAMの内訳 ── Availableが少ないだけでは判断しない

物理RAMは、ユーザープロセスのWorking Setだけに使われるわけではありません。

  • 各プロセスのWorking Set
  • システムファイルキャッシュ
  • Standby、Modified、Free、Zeroedなどのページリスト
  • カーネルのPaged Pool / Nonpaged Pool
  • デバイスドライバーが保持するメモリ
  • メモリ圧縮のストア
  • GPUやデバイスと共有・予約される領域
  • ハードウェア予約

7.1. Availableには再利用可能なキャッシュも含まれる

WindowsのAvailable MBytesは、単なる完全未使用RAMだけではありません。Free、Zeroedに加え、必要なら再利用できるStandbyページも含む指標です。12

  • Free: 現在どの用途にも割り当てられていないページ
  • Zeroed: 別プロセスへ安全に渡せるようゼロ化済みのページ
  • Standby: Working Setから外れたが、内容をRAM上にキャッシュしているページ
  • Modified: 内容が変更され、再利用前に適切な裏付けへ書き戻す必要があるページ
Working Setとページリストの間の移動使用中ページが未変更ならStandbyへ、変更済みならModifiedへ移り、再アクセスや書き戻し、再利用を経る流れを示す未変更ページを外す変更済みページを外す書き戻し完了再アクセス別用途へ再利用ゼロ化割り当て後にアクセスWorking Set: 使用中Standby: 内容を保持した再利用候補Modified: 書き戻し待ち別用途へ割り当てFree: 未使用Zeroed: 新規割り当て可能Availableに含まれる

図8: Availableには完全な空きだけでなく、必要なら再利用できるStandbyも含まれる。

「空きRAMを増やすためにキャッシュを全部捨てる」ことが常に得ではありません。Standby上に必要なデータが残っていれば、再アクセス時にディスクを読まず素早くWorking Setへ戻せます。

したがって、タスクマネージャーでFreeが少なくても、Availableが十分あり、ハードページフォールトやディスク待ちが問題になっていなければ、WindowsがRAMをキャッシュとして有効利用しているだけかもしれません。

7.2. 大きなプロセスがないのにRAMが減る場合

各プロセスのPrivate Working Setを足しても説明できないメモリ消費は珍しくありません。

  • ファイルキャッシュやメモリマップトファイル
  • Nonpaged Pool / Paged Pool
  • ドライバーがロックしたページ
  • 共有ページ
  • メモリ圧縮
  • 仮想化やGPU関連の割り当て

この場合、プロセス一覧を眺め続けるより、SysinternalsのRAMMapでUse Counts、Processes、Priority Summary、File Summaryを確認します。RAMMapは物理メモリを用途別・ページリスト別・ファイル別に分解するための公式ツールです。13

Nonpaged Poolだけが増え続けるような場合は、ユーザーモードアプリのPrivate Bytesではなく、ドライバーやカーネル側のリークを疑う段階です。

8. Page Fault ── 多いだけでは異常ではない

プロセスが、現在Working SetにないページへアクセスするとPage Faultが発生します。名前に「Fault」とありますが、例外的な故障ではなく、仮想メモリを動かす通常の仕組みです。1

8.1. ソフトページフォールト

ディスクを読まずに解決できるものです。

  • ページがStandbyやTransitionに残っている
  • 別プロセスのWorking Setに同じ共有ページがある
  • Commit済みページへ初めてアクセスし、ゼロページを割り当てる
  • メモリマネージャーの先読みで既にRAMにある

このため、\Memory\Page Faults/secが大きくても、ディスクI/Oや遅延が発生しているとは限りません。

8.2. ハードページフォールト

ディスク上のBacking Storeから内容を読む必要があるものです。読み元はページファイルに限りません。

  • .exe.dllのコード・データ
  • メモリマップトファイル
  • ページファイル
ソフトページフォールトとハードページフォールトの分岐Working Setにないページへアクセスしたとき、ストレージI/Oが不要ならソフトページフォールト、必要ならハードページフォールトとして処理するいいえ: Standby・共有・Demand-zeroなどはいWorking SetにないページへアクセスストレージI/Oが必要かソフトページフォールトディスクを読まずWorking Setへハードページフォールトどこから読むかEXE / DLLメモリマップトファイルページファイル読み込み後Working Setへ

図9: Page Faultという名前だけではディスクI/Oの有無を判定できない。

Microsoftは、ハードフォールトを測るカウンターとして\Memory\Pages/sec\Memory\Page Reads/sec\Memory\Pages Input/secなどを挙げています。これらが高くても必ず低メモリとは限らないため、Available MBytes、ディスク遅延、実際の応答時間と相関させます。6

8.3. 一律のしきい値を置かない

「Page Faults/secが1000を超えたら異常」のような固定値は、ストレージ、ページサイズ、ワークロード、アクセス局所性で意味が変わります。

実務では、次を同じ時間軸へ並べます。

  • Memory\Available MBytes
  • Memory\Pages Input/sec
  • Memory\Page Reads/sec
  • 対象ディスクのRead latency / Queue
  • 対象プロセスのWorking Set・Private Bytes
  • アプリの処理時間、タイムアウト、UI応答

負荷増加と同時にAvailableが減り、Pages Input/secとディスク待ちが上がり、処理時間も悪化するなら、物理メモリ圧迫によるページングを疑う根拠が揃います。

9. どの画面・ツールで何を見るか

知りたいこと 最初に見る指標 主なツール
今、対象プロセスがRAMへ載せている量 Working Set タスクマネージャー、Process Explorer、Get-Process
そのうちプロセス固有のRAM Private Working Set / Working Set - Private タスクマネージャーの詳細列、Process Explorer、PerfMon
対象プロセス固有のコミット量 Private Bytes / Commit Size Process Explorer、PerfMon、VMMap、Get-Process
プロセスの仮想アドレス範囲 Virtual Bytes / Size Process Explorer、VMMap、Get-Process
システム全体のコミット余力 Committed Bytes / Commit Limit タスクマネージャー[パフォーマンス]、PerfMon
物理RAMの再利用余力 Available MBytes タスクマネージャー、PerfMon
Standby・Modified・ファイルキャッシュの内訳 ページリスト・用途別内訳 RAMMap
Private Bytesの中で何が増えたか Heap / Private Data / Managed Heap等 VMMap、WinDbg、ランタイム別ダンプ
ディスクを伴うページング Pages Input/sec、Page Reads/sec、ディスク遅延 PerfMon、WPR/WPA
Windowsのメモリ調査ツールを選ぶ対象が1プロセスかシステム全体か、一時点か時系列か、ランタイム内部まで追うかによって使うツールを選ぶはい一時点の内訳時系列システム全体物理RAMの内訳CPU・I/O・待機も含む時間軸.NETヒープネイティブヒープ何を切り分けたいか対象は1プロセスか一時点か時系列かVMMapPerfMon / PowerShell物理RAMの内訳か時間軸かRAMMapWPR / WPAランタイム内部の保持まで追うかdotnet-dump / PerfViewWinDbg / Application Verifier

図10: 最初に対象範囲と時間軸を決めると、必要なツールを過不足なく選べる。

9.1. タスクマネージャー

タスクマネージャーでは、画面を分けて見ます。

  • [プロセス]または[詳細]: 個別プロセスのWorking Set系、Commit Size系
  • [パフォーマンス]→[メモリ]: システム全体のIn use、Available、Committed、Cached、Paged pool、Non-paged pool

「メモリ」という列名だけで判断せず、[詳細]タブの列見出しを右クリックして、Working Set、Peak Working Set、Commit Sizeなど必要な列を追加します。Windowsのバージョンや表示言語によって列名は多少異なるため、列の意味を確認してから記録します。

9.2. PowerShellで時系列を取る

対象プロセスのIDが分かっているなら、Get-ProcessでWorking Set、Private Bytes、Virtual Bytesの傾きを同時に取れます。

param(
    [Parameter(Mandatory)]
    [int]$ProcessId,

    [int]$IntervalSeconds = 5,
    [int]$SampleCount = 60
)

$samples = for ($i = 0; $i -lt $SampleCount; $i++) {
    $process = Get-Process -Id $ProcessId -ErrorAction Stop

    [pscustomobject]@{
        Timestamp      = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
        ProcessId      = $process.Id
        WorkingSetMB   = [math]::Round($process.WorkingSet64 / 1MB, 1)
        PrivateBytesMB = [math]::Round($process.PrivateMemorySize64 / 1MB, 1)
        VirtualBytesMB = [math]::Round($process.VirtualMemorySize64 / 1MB, 1)
        Handles        = $process.HandleCount
        Threads        = $process.Threads.Count
    }

    Start-Sleep -Seconds $IntervalSeconds
}

$samples | Format-Table -AutoSize
$samples | Export-Csv .\memory-samples.csv -NoTypeInformation -Encoding utf8

.NETProcess.WorkingSet64はWorking Set、PrivateMemorySize64はPrivate Bytes、VirtualMemorySize64はVirtual Bytesに対応します。141516

複数インスタンスがあるアプリでは、名前ではなくPIDで追ってください。再起動でPIDが変わる長期監視では、起動時刻やサービス名などを記録して対象を取り違えない設計が必要です。

9.3. PerfMonでシステムとプロセスを同じ時間軸にする

最低限、次を同時に記録すると切り分けやすくなります。

\Process(<対象>)\ID Process
\Process(<対象>)\Working Set
\Process(<対象>)\Working Set - Private
\Process(<対象>)\Private Bytes
\Process(<対象>)\Virtual Bytes

\Memory\Available MBytes
\Memory\Committed Bytes
\Memory\Commit Limit
\Memory\Pages Input/sec
\Memory\Page Reads/sec
\Memory\Pool Nonpaged Bytes
\Memory\Pool Paged Bytes

同名プロセスが複数ある場合や監視中に再起動する場合、Process(name)Process(name#N)のインスタンス名だけでは対象を固定できません。各サンプルでID Processも記録し、その値が追跡対象のPIDと一致するインスタンスだけを採用します。PIDが変わる再起動をまたぐ場合は、切り替わった時刻も別途記録します。

Windowsのパフォーマンスカウンター名は表示言語によってローカライズされる場合があります。PowerShellで英語名を直接指定して見つからない場合は、PerfMonのGUIから追加するか、Get-Counter -ListSet *でローカル環境の名前を確認します。

9.4. VMMapとRAMMapの役割を混同しない

  • VMMap: 1プロセスの仮想メモリとWorking Setを、Heap、Image、Mapped File、Private Data、Managed Heapなどへ分解する
  • RAMMap: システム全体の物理RAMを、用途、ページリスト、プロセス、ファイルへ分解する

「このプロセスのPrivate Bytesは何で増えたか」はVMMap、「プロセス一覧で説明できないRAMは何に使われたか」はRAMMapです。1713

10. 数字の組み合わせから症状を読む

観測した形 まず考えること 次に確認するもの
Working Setが増え、Private Bytesは安定 既存ページへの初回アクセス、共有DLL、マップトファイル、ファイルキャッシュ VMMapのImage / Mapped File、Pages Input/sec
Private Bytesが増え、Working Setは安定 PrivateなCommitは増えたが、非常駐またはTrimされている VMMapのHeap / Private Data / Managed Heap
両方が起動直後に増え、その後横ばい JIT、キャッシュ、プール、初期化のウォームアップ 同じ負荷を追加して再び増えるか
負荷のたびPrivate Bytesの底が上がる リーク、上限なしキャッシュ、解放後も保持するアロケーター 前後のVMMapスナップショット、ヒープダンプ
Working Setだけ急に下がり、操作で戻る OSまたはアプリがWorking SetをTrimした Private Bytes、Pages Input/sec、応答時間
Committed X/YのXがYへ近づく システム全体のコミット圧迫 Private Bytes上位、Paged/Nonpaged Pool、ページファイル設定
Availableが低く、Pages Input/secとディスク遅延が高い 物理RAM圧迫とハードページング Working Set上位、RAMMap、ワークロード相関
RAM使用率は高いが大きなプロセスがない キャッシュ、共有ページ、カーネルプール、ドライバー、圧縮等 RAMMap、Pool Nonpaged/Paged Bytes
空きRAMがあるのに32bitアプリだけ失敗 仮想アドレス空間上限・断片化 VMMapのFree/Reserved、実行ファイルのLAA設定
Private Bytesは高いが処理を繰り返しても増えない 高水位を保持するプール・キャッシュの可能性 上限、再利用状況、ピーク後の安定性

この表で最も大切なのは、単独の値ではなく組み合わせで読むことです。

11. メモリリーク調査の実務手順

11.1. まず再現条件と定常点を決める

「数日で増える」だけでは比較できません。

  • 起動後のウォームアップをどこまで含めるか
  • 1サイクルの操作内容
  • 1サイクル後に何秒待つか
  • キャッシュ上限へ達するまで何回必要か
  • 正常版・問題版で同じ入力を使えるか

を決めます。

11.2. プロセスとシステムを同時に記録する

最低でも次を同じ時刻で残します。

  • 対象のWorking Set
  • 対象のPrivate Bytes
  • 対象のVirtual Bytes
  • システムのCommitted Bytes / Commit Limit
  • Available MBytes
  • Pages Input/sec
  • ハンドル数、スレッド数
  • 操作回数や処理件数

プロセスのPrivate Bytesが安定しているのにシステムのCommitだけ増えるなら、別プロセス、カーネル、ドライバー、共有セクションなど視野を広げる必要があります。

11.3. 増えている「次元」を先に決める

  • Working Setだけ: 常駐ページ、共有・ファイル由来、Trimと再読込
  • Private Bytes: プロセス固有コミット
  • Virtual Bytesだけ: Reserve、マッピング、アドレス空間断片化
  • System Commitだけ: 他プロセスやカーネル側も含む
  • Nonpaged Pool: ドライバー・カーネル側
  • Handles / GDI / USER: メモリ以外の資源リーク

この順序を飛ばしていきなりダンプを取ると、対象が違うまま大量の情報を読むことになります。

11.4. 内訳へ進む

  • ネイティブプロセス: VMMap、WinDbg、Application Verifier、ヒープトレース
  • .NET: dotnet-countersdotnet-gcdumpdotnet-dump、PerfView
  • システム全体: RAMMap、PerfMon、WPR/WPA
  • カーネルプール: PoolMon、WinDbg

VMMapは、プロセスのコミット済み仮想メモリと、それぞれに割り当てられたWorking Setを種類別に表示します。Private Bytesの増加を「Heap」「Private Data」「Managed Heap」「Mapped File」のどこまで絞れるかで、その後の調査コストが大きく変わります。17

11.5. 修正後は同じ条件で傾きを比較する

修正前後でピーク値が違うだけでは十分ではありません。初期値が違えば簡単に逆転します。

  • 同じ起動状態
  • 同じ入力
  • 同じ操作回数
  • 同じ待機時間
  • 同じサンプリング間隔

で、各サイクル後の底値と傾きを比較します。リーク修正の証明は「最大値が小さくなった」ではなく、同じ負荷を繰り返しても増加が収束するようになったです。

12. よくある誤解を言い換える

誤解1: タスクマネージャーのメモリ=アプリが確保した全量

言い換え: どの列かを確認する。Working Set系なら現在RAMに常駐する量、Commit Size系ならプロセス固有のコミット量です。

誤解2: Private Bytes=ページファイル上のバイト数

言い換え: Private BytesはPrivateなCommit Chargeです。RAMにあるページも、必要時にページファイルで支えられるページも含む論理的な約束量です。

誤解3: Commit X/Y=ページファイル使用量/ページファイル容量

言い換え: Xはシステム全体のCommit Charge、YはCommit Limitです。ページファイルはYを拡張しますが、Xがそのままディスク上の使用量になるわけではありません。

誤解4: Page Faults/secが高い=ディスクへスワップしている

言い換え: ソフトフォールトも含みます。ディスクI/Oを伴うかはPages Input/secやPage Reads/secとディスク遅延で確認します。

誤解5: Free RAMが少ない=メモリ不足

言い換え: Available、Standby、ハードページング、応答時間を見る。再利用可能なキャッシュでRAMを埋めるのは正常です。

誤解6: Working Setを小さくできた=メモリリークを直した

言い換え: ページをRAMから追い出しただけかもしれません。Private Bytesやヒープ内の保持が減ったかを確認します。

誤解7: Private Bytesが増えた=リーク確定

言い換え: 同じワークロードを繰り返したときに収束するか、どの種類のメモリが増えたか、解放可能なキャッシュかを確認して初めて判断できます。

13. まとめ

  • Windowsの「メモリ使用量」は1つの数字ではありません。アドレス空間、コミット、RAM常駐、共有可能性を分けて考えます。
  • Working Setは現在RAMにあるページで、PrivateとSharedの両方を含みます。Private Working Setはそのうちプロセス固有の常駐ページです。
  • Private Bytesはプロセス固有のCommit Chargeで、現在RAMにある量でも、ページファイルへ実際に書かれた量でもありません。
  • Committed X/Yはシステム全体のCommit Charge / Commit Limitです。ページファイルは主にCommit Limit、変更済みページの退避、クラッシュダンプを支えます。
  • Reserveした仮想アドレス、Commitしたページ、実際に触れてWorking Setへ入ったページは別段階です。
  • Page Faultは通常動作で、ソフトフォールトはディスクを読みません。ハードフォールトもページファイルだけでなく、EXE、DLL、マップトファイルから発生します。
  • メモリリークは一時点の大きさではなく、同じ負荷後の底値と傾き、そして内訳で証明します。
  • 個別プロセスの内訳はVMMap、システム全体の物理RAMはRAMMap、時系列はPerfMon、ランタイム内部は専用ダンプツールへ進むのが基本です。

次にタスクマネージャーで「メモリが増えている」と気付いたら、最初にこう問い直してください。

増えているのは、Working Setか、Private Bytesか、Virtual Bytesか、System Commitか。

この問いだけで、調査の入口はかなり正確になります。

関連記事

関連する相談領域

合同会社小村ソフトでは、Windowsアプリのメモリ増加、長期稼働後の性能劣化、32bitプロセスのOutOfMemory、顧客環境でのみ発生するメモリ不足について、PerfMon・VMMap・RAMMap・WinDbg・.NET診断ツールを組み合わせた原因調査を扱っています。単に「メモリが多い」で終わらせず、どの領域が、どの操作で、なぜ増え、どこから参照・保持されているかまで切り分けます。

参考リンク

  1. Microsoft Learn, Working Set. プロセスのWorking Setが現在物理メモリへ常駐するページの集合であること、共有ページを含むこと、ソフト・ハードページフォールトの違い、Transitionページ、Working Setからのページ除去について。  2 3 4 5

  2. Microsoft Learn, PROCESS_MEMORY_COUNTERS_EX2 structure. WorkingSetSize、PrivateWorkingSetSize、PrivateUsage、SharedCommitUsageの定義、PagefileUsageとPrivateUsageがともにプロセスのCommit Chargeを表すことについて。  2 3 4

  3. Microsoft Learn, Introduction to page files. ページファイルが変更済みページの退避、システムクラッシュダンプ、System Commit Limitの拡張を支えること、System Commit ChargeとCommit Limitの定義、タスクマネージャーおよび性能カウンターでの測定について。  2 3 4

  4. Microsoft Learn, Page State. 仮想ページのFree、Reserved、Committed各状態と、Reservedページには物理ストレージが関連付けられずアクセスできないことについて。  2

  5. Microsoft Learn, VirtualAlloc function. MEM_RESERVEとMEM_COMMITの違い、Commit時にシステム全体のメモリとページファイルからChargeされること、実際の物理ページは初回アクセスまで割り当てられない場合があることについて。  2 3

  6. Microsoft Learn, How to determine the appropriate page file size for 64-bit versions of Windows. ページファイルのサイズがピークCommit Chargeとクラッシュダンプ要件に依存すること、ハードページフォールトの読み元がページファイルに限らずEXE・DLL・メモリマップトファイルを含むこと、関連する性能カウンターについて。  2 3 4 5 6

  7. Microsoft Learn, Virtual Address Space. 各プロセスが独立した仮想アドレス空間とページテーブルを持ち、仮想アドレスが物理アドレスそのものではないことについて。 

  8. Microsoft Learn, Memory Limits for Windows and Windows Server Releases. 32bitプロセスのユーザーモード仮想アドレス空間が通常2GBで、64bit Windows上ではIMAGE_FILE_LARGE_ADDRESS_AWAREの有無により2GBまたは4GBとなることについて。 

  9. Microsoft Learn, SetProcessWorkingSetSize function. Working Setの最小・最大値が常駐を保証するものではないこと、Working Setを空にできること、過大な設定や操作がシステム性能を悪化させ得ることについて。 

  10. Microsoft Learn, Memory Performance Information. Windowsの性能カウンター、メモリ管理API、タスクマネージャーの表示の対応関係、ProcessのWorking Set・Working Set - Private・Private Bytes、SystemのCommitted Bytes・Commit Limitについて。 

  11. Microsoft Learn, MapViewOfFile function. FILE_MAP_COPYでは全ページがCopy-on-writeになり得るため、ビュー全体をページファイルで裏付けられるようマッピング時点でCommit Chargeが確保されることについて。 

  12. Microsoft Learn, Understanding Node Metrics and Properties in HPC Cluster Manager. Available Physical MemoryがZeroed、Free、Standby各リストの合計として計算されることと、それぞれのページリストの意味について。 

  13. Microsoft Sysinternals, RAMMap. Windowsの物理メモリ使用量を用途、ページリスト、プロセス、優先度、物理ページ、ファイル単位で分析する機能について。  2

  14. Microsoft Learn, Process.WorkingSet64 Property. WorkingSet64がプロセスのWorking Setをバイト単位で返し、ProcessのWorking Set性能カウンターに対応することについて。 

  15. Microsoft Learn, Process.PrivateMemorySize64 Property. PrivateMemorySize64が他プロセスと共有できないプロセス固有メモリを返し、Private Bytes性能カウンターに対応することについて。 

  16. Microsoft Learn, Process.VirtualMemorySize64 Property. VirtualMemorySize64がプロセスの仮想メモリ量を返し、Virtual Bytes性能カウンターに対応することについて。 

  17. Microsoft Sysinternals, VMMap. プロセスのコミット済み仮想メモリを種類別に分解し、それぞれへ割り当てられた物理メモリ(Working Set)と詳細なメモリマップを表示する機能について。  2

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

この記事は次のサービスページにつながります。近い入口からご覧ください。

よくある質問

この記事のテーマについて、相談時によくある質問をまとめています。

タスクマネージャーの「メモリ」は、アプリが確保している全メモリですか?
いいえ。タスクマネージャーにはWorking Set系、Private Working Set系、Commit Sizeなど複数のメモリ列があり、どの画面・列を見るかで意味が変わります。Working Setは現在RAMに載っているページ、Private BytesまたはCommit Sizeはそのプロセス固有のコミット量です。1つの「メモリ」列を、アプリが確保した全容量やリーク量と読み替えないでください。
Working SetとPrivate Bytesは何が違うのですか?
Working Setは、そのプロセスから見えていて現在物理RAMに常駐しているページの量で、DLLやメモリマップトファイルなどの共有ページも含みます。Private Bytesは、そのプロセスだけが使うコミット済みメモリの量で、現在RAMに常駐しているかどうかは問いません。したがって両者は同じ値にならず、単純な大小関係でもありません。
タスクマネージャーの「コミット済み 18/32GB」は、18GBをページファイルに書いているという意味ですか?
違います。左側はシステム全体が現在約束しているコミット量、右側はシステムが支えられるコミット上限です。上限は概ねRAMとページファイルの合計で決まりますが、左側の全量がページファイル上にあるわけではありません。多くのコミット済みページはRAMにあり、まだ一度も物理ページを割り当てられていないページもあります。一方、EXE・DLL・メモリマップトファイルのように元ファイルから再読込できるページは、Working Setを増やしてもPrivateなCommitを同じだけ増やすとは限りません。
空きRAMがあるのにOutOfMemoryになることはありますか?
あります。32bitプロセスの仮想アドレス空間不足、連続した空きアドレス範囲の不足、システムのコミット上限、Job Objectやランタイム固有の上限など、物理RAM以外にも割り当て失敗の条件があるためです。特に64bit Windows上の32bitプロセスは、Large Address Awareでなければ通常2GBのユーザーモード仮想アドレス空間が上限です。
ページファイルを無効にするとWindowsは速くなりますか?
一般には、速くなると決めつけられません。ページファイルを無効にするとシステムのコミット上限が下がり、使われていない変更済みページをRAMから退避しにくくなり、クラッシュダンプの構成にも影響します。ページファイルのサイズは、ピーク時のコミット量と必要なクラッシュダンプを測ったうえで決めるべきで、根拠なく無効化する設定ではありません。
Page Faults/secが多ければメモリ不足ですか?
それだけでは判断できません。ページフォールトには、RAM内のStandbyページや別プロセスと共有中のページで解決できるソフトフォールトと、ディスクから読み込むハードフォールトがあります。Page Faults/sec単独ではなく、Pages Input/secやPage Reads/sec、Available MBytes、ディスク遅延、処理時間を同じ時間軸で確認してください。

著者プロフィール

記事の著者プロフィールページです。

小村 豪

合同会社小村ソフト 代表

Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。

ブログ一覧に戻る