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は「システム全体が約束した量」です。
flowchart TB
accTitle: Windowsの主要なメモリ指標を選ぶ
accDescr: 知りたい対象がRAM常駐量、プロセス固有のコミット量、システム全体のコミット量、仮想アドレス範囲のどれかによって見る指標が変わる
question["「メモリ使用量」で何を知りたいか"]
question -->|今RAMにある量| workingSet["Working Set"]
question -->|プロセス固有の約束量| privateBytes["Private Bytes"]
question -->|システム全体の約束量| systemCommit["System Commit"]
question -->|確保したアドレス範囲| virtualBytes["Virtual Bytes / Reserved"]
workingSet --> resident["物理RAMへの常駐"]
privateBytes --> privateCommit["プロセス固有のCommit"]
systemCommit --> commitLimit["Commit Limitと比較"]
virtualBytes --> addressSpace["仮想アドレス空間"]
図1: 「メモリが多い」という観察を、最初に4種類の問いへ分解する。
2. 「メモリ使用量」を4つの軸に分ける
最初に、Windowsのメモリを「1本の棒」ではなく、4つの軸で考えます。
flowchart TB
accTitle: 1ページを分類する4つの独立した軸
accDescr: 仮想アドレスの状態、コミット済みページの裏付け、物理RAMへの常駐、他プロセスとの共有可能性を別々に確認する
page["1ページを4つの軸で見る"]
page --> address["アドレス状態"]
address --> addressValues["Free / Reserved / Committed"]
page --> backing["裏付け"]
backing --> backingValues["Page-file-backed / File-backed"]
page --> residentAxis["RAM常駐"]
residentAxis --> residentValues["Resident / Not resident"]
page --> sharing["共有可能性"]
sharing --> sharingValues["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常駐 | 含む | 原則含まない | 原則含まない | 含む |
| 予約済みだが未コミット | 含まない | 含まない | 含まない | 含み得る |
| 未使用のアドレス範囲 | 含まない | 含まない | 含まない | 通常含まない |
flowchart TB
accTitle: ページの種類と主要メモリ指標の対応
accDescr: Privateで常駐するページ、Privateで非常駐のページ、共有で常駐するページ、予約のみの範囲がどの指標へ含まれるかを示す
privateResident["Private・Commit済み・RAM常駐"]
privateNonresident["Private・Commit済み・RAM非常駐"]
sharedResident["共有ページ・RAM常駐"]
reservedOnly["Reserved・未Commit"]
workingSet["Working Set"]
privateWorkingSet["Private Working Set"]
privateBytes["Private Bytes"]
virtualBytes["Virtual Bytes系"]
privateResident --> workingSet
privateResident --> privateWorkingSet
privateResident --> privateBytes
privateResident --> virtualBytes
privateNonresident --> privateBytes
privateNonresident --> virtualBytes
sharedResident --> workingSet
sharedResident --> virtualBytes
reservedOnly --> virtualBytes
図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は「住所を押さえた」だけ
VirtualAllocのMEM_RESERVEは、将来使うために連続した仮想アドレス範囲を確保します。この段階ではページへ物理ストレージは関連付けられず、その範囲を読み書きすることもできません。45
たとえば、データベースやランタイムが将来の成長用に8GBのアドレス範囲をReserveしていても、それだけで8GBのRAMや8GBのPrivate Bytesを消費したことにはなりません。
3.3. Committedは「必要になったとき支える」と約束した状態
MEM_COMMITは、その仮想ページをCommitted状態にし、Windowsが必要な裏付けを提供すると約束する操作です。実際に読み取り・書き込み・実行が許可されるかは、PAGE_READONLY、PAGE_READWRITE、PAGE_EXECUTE、PAGE_NOACCESSなどのページ保護で別に決まり、Commit済みであること自体は「読み書き可能」を意味しません。コミットした時点でシステムのCommit Chargeへ計上されますが、実際の物理ページは初回アクセス時まで割り当てられない場合があります。初めて触れたページはゼロで初期化され、Demand-zero faultを経てWorking Setへ入ります。51
したがって、同じ「確保した」でも次の3段階があります。
flowchart TB
accTitle: ReserveからCommitとRAM常駐までの3段階
accDescr: 仮想アドレスを予約し、ページをコミットし、初回アクセスで物理ページが割り当てられてWorking Setへ入る流れを示す
reserve["MEM_RESERVE: アドレス範囲を確保"]
reserve -.-> virtualMetric["Virtual Bytes系に反映"]
reserve -->|MEM_COMMIT| committed["Commit済み: ページ保護に応じてアクセス可能"]
committed -.-> commitMetric["Private Bytes / System Commitに反映"]
committed -->|初回アクセス・Demand-zero fault| resident["物理ページを割り当ててRAM常駐"]
resident -.-> workingSetMetric["Working Setに反映"]
committed -.->|未アクセスなら| nonresident["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の下降は「アプリが解放した」、上昇は「アプリが新しく確保した」とは限りません。
flowchart TB
accTitle: Working Setだけが増減する典型的な流れ
accDescr: 同じCommit済みページが初回アクセスでRAMへ入り、Trimで非常駐になり、再アクセスで戻る間もPrivate Bytesは計上され続ける
committed["同じCommit済みページ"]
committed -->|初回アクセス| resident["RAM常駐"]
resident -->|メモリ圧迫でTrim| nonresident["非常駐"]
nonresident -->|再アクセスでPage Fault| resident
resident -.-> inWorkingSet["Working Setに含む"]
nonresident -.-> outsideWorkingSet["Working Setに含まない"]
committed -.-> privateBytes["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を強制的に小さくしてもリークは直らない
EmptyWorkingSetやSetProcessWorkingSetSizeを使うと、プロセスの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には、代表的には次が影響します。
HeapAlloc、malloc、newなどが使うネイティブヒープのコミット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が高いことだけではリークを証明できません。見るべきは、
- 同じ処理を同じ回数だけ繰り返す
- 処理後に同じ待機時間を置く
- Private Bytesが同じ水準へ戻るか、一定値で頭打ちになるか
- VMMapやヒープダンプで、どの領域・型が増えたかを確認する
という時間差比較です。
flowchart TB
accTitle: freeやGCの後もPrivate Bytesが下がらない理由
accDescr: アプリが不要にした領域をアロケーターがOSへ返す場合と再利用用に保持する場合でPrivate Bytesの変化が異なる
release["アプリがfree / GCで不要にする"]
release --> decision{"アロケーターはOSへ返すか"}
decision -->|Decommit / Release| returned["Commit Chargeが減る"]
returned --> lower["Private Bytesが下がる"]
decision -->|再利用用に保持| retained["領域はCommit済みのまま"]
retained --> high["Private Bytesは高止まり"]
retained --> reasons["プール・キャッシュ・断片化"]
図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
flowchart TB
accTitle: System Commit ChargeとCommit Limitの関係
accDescr: プロセス固有、共有セクション、カーネルのコミットが現在値Xを作り、物理RAMとページファイルが上限Yを支える
processCommit["各プロセスのPrivate Commit"] --> charge["System Commit Charge: X"]
sharedCommit["ページファイルで裏付ける共有セクションのCommit"] --> charge
kernelCommit["カーネルのCommit"] --> charge
physicalRam["物理RAM"] --> limit["System Commit Limit: Y"]
pageFiles["ページファイル"] --> limit
charge -->|XはYを超えられない| limit
図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つの役割
ページファイルには主に次の役割があります。
- Commit Limitを拡張する
- 使用頻度の低い変更済みページをRAMから退避できるようにする
- 構成に応じてシステムクラッシュダンプを支える
ページファイルを無効化すると、ディスク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: 内容が変更され、再利用前に適切な裏付けへ書き戻す必要があるページ
flowchart TB
accTitle: Working Setとページリストの間の移動
accDescr: 使用中ページが未変更ならStandbyへ、変更済みならModifiedへ移り、再アクセスや書き戻し、再利用を経る流れを示す
workingSet["Working Set: 使用中"]
workingSet -->|未変更ページを外す| standby["Standby: 内容を保持した再利用候補"]
workingSet -->|変更済みページを外す| modified["Modified: 書き戻し待ち"]
modified -->|書き戻し完了| standby
standby -->|再アクセス| workingSet
standby -->|別用途へ再利用| reused["別用途へ割り当て"]
free["Free: 未使用"] -->|ゼロ化| zeroed["Zeroed: 新規割り当て可能"]
zeroed -->|割り当て後にアクセス| workingSet
standby -.-> available["Availableに含まれる"]
free -.-> available
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のコード・データ- メモリマップトファイル
- ページファイル
flowchart TB
accTitle: ソフトページフォールトとハードページフォールトの分岐
accDescr: Working Setにないページへアクセスしたとき、ストレージI/Oが不要ならソフトページフォールト、必要ならハードページフォールトとして処理する
access["Working Setにないページへアクセス"] --> storageIo{"ストレージI/Oが必要か"}
storageIo -->|いいえ: Standby・共有・Demand-zeroなど| soft["ソフトページフォールト"]
soft --> resident["ディスクを読まずWorking Setへ"]
storageIo -->|はい| hard["ハードページフォールト"]
hard --> source{"どこから読むか"}
source --> image["EXE / DLL"]
source --> mapped["メモリマップトファイル"]
source --> pagefile["ページファイル"]
image --> loaded["読み込み後Working Setへ"]
mapped --> loaded
pagefile --> loaded
図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 MBytesMemory\Pages Input/secMemory\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 |
flowchart TB
accTitle: Windowsのメモリ調査ツールを選ぶ
accDescr: 対象が1プロセスかシステム全体か、一時点か時系列か、ランタイム内部まで追うかによって使うツールを選ぶ
question["何を切り分けたいか"]
question --> processScope{"対象は1プロセスか"}
processScope -->|はい| processTime{"一時点か時系列か"}
processTime -->|一時点の内訳| vmmap["VMMap"]
processTime -->|時系列| perfmon["PerfMon / PowerShell"]
processScope -->|システム全体| systemView{"物理RAMの内訳か時間軸か"}
systemView -->|物理RAMの内訳| rammap["RAMMap"]
systemView -->|CPU・I/O・待機も含む時間軸| wpa["WPR / WPA"]
question --> runtime{"ランタイム内部の保持まで追うか"}
runtime -->|.NETヒープ| dotnet["dotnet-dump / PerfView"]
runtime -->|ネイティブヒープ| native["WinDbg / 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
.NETのProcess.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-counters、dotnet-gcdump、dotnet-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か。
この問いだけで、調査の入口はかなり正確になります。
関連記事
- .NETでGC待ちとメモリリークを見分ける ── 増えるメモリを観測・比較・証明する実務手順
- Process Explorer / Handle / VMMap実践 ── ハング・リーク・「ファイルが使用中」を今この瞬間の状態から追う
- 共有メモリの落とし穴とベストプラクティス ── Windowsでのプロセス間共有を安全に設計する
- Windows Cache Managerの遅延書き込み ── WriteFileからディスク反映まで
- Windowsアプリのハンドルリークで長期稼働後にクラッシュする原因と対策
関連する相談領域
合同会社小村ソフトでは、Windowsアプリのメモリ増加、長期稼働後の性能劣化、32bitプロセスのOutOfMemory、顧客環境でのみ発生するメモリ不足について、PerfMon・VMMap・RAMMap・WinDbg・.NET診断ツールを組み合わせた原因調査を扱っています。単に「メモリが多い」で終わらせず、どの領域が、どの操作で、なぜ増え、どこから参照・保持されているかまで切り分けます。
参考リンク
-
Microsoft Learn, Working Set. プロセスのWorking Setが現在物理メモリへ常駐するページの集合であること、共有ページを含むこと、ソフト・ハードページフォールトの違い、Transitionページ、Working Setからのページ除去について。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, PROCESS_MEMORY_COUNTERS_EX2 structure. WorkingSetSize、PrivateWorkingSetSize、PrivateUsage、SharedCommitUsageの定義、PagefileUsageとPrivateUsageがともにプロセスのCommit Chargeを表すことについて。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Introduction to page files. ページファイルが変更済みページの退避、システムクラッシュダンプ、System Commit Limitの拡張を支えること、System Commit ChargeとCommit Limitの定義、タスクマネージャーおよび性能カウンターでの測定について。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Page State. 仮想ページのFree、Reserved、Committed各状態と、Reservedページには物理ストレージが関連付けられずアクセスできないことについて。 ↩ ↩2
-
Microsoft Learn, VirtualAlloc function. MEM_RESERVEとMEM_COMMITの違い、Commit時にシステム全体のメモリとページファイルからChargeされること、実際の物理ページは初回アクセスまで割り当てられない場合があることについて。 ↩ ↩2 ↩3
-
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
-
Microsoft Learn, Virtual Address Space. 各プロセスが独立した仮想アドレス空間とページテーブルを持ち、仮想アドレスが物理アドレスそのものではないことについて。 ↩
-
Microsoft Learn, Memory Limits for Windows and Windows Server Releases. 32bitプロセスのユーザーモード仮想アドレス空間が通常2GBで、64bit Windows上ではIMAGE_FILE_LARGE_ADDRESS_AWAREの有無により2GBまたは4GBとなることについて。 ↩
-
Microsoft Learn, SetProcessWorkingSetSize function. Working Setの最小・最大値が常駐を保証するものではないこと、Working Setを空にできること、過大な設定や操作がシステム性能を悪化させ得ることについて。 ↩
-
Microsoft Learn, Memory Performance Information. Windowsの性能カウンター、メモリ管理API、タスクマネージャーの表示の対応関係、ProcessのWorking Set・Working Set - Private・Private Bytes、SystemのCommitted Bytes・Commit Limitについて。 ↩
-
Microsoft Learn, MapViewOfFile function.
FILE_MAP_COPYでは全ページがCopy-on-writeになり得るため、ビュー全体をページファイルで裏付けられるようマッピング時点でCommit Chargeが確保されることについて。 ↩ -
Microsoft Learn, Understanding Node Metrics and Properties in HPC Cluster Manager. Available Physical MemoryがZeroed、Free、Standby各リストの合計として計算されることと、それぞれのページリストの意味について。 ↩
-
Microsoft Sysinternals, RAMMap. Windowsの物理メモリ使用量を用途、ページリスト、プロセス、優先度、物理ページ、ファイル単位で分析する機能について。 ↩ ↩2
-
Microsoft Learn, Process.WorkingSet64 Property.
WorkingSet64がプロセスのWorking Setをバイト単位で返し、ProcessのWorking Set性能カウンターに対応することについて。 ↩ -
Microsoft Learn, Process.PrivateMemorySize64 Property.
PrivateMemorySize64が他プロセスと共有できないプロセス固有メモリを返し、Private Bytes性能カウンターに対応することについて。 ↩ -
Microsoft Learn, Process.VirtualMemorySize64 Property.
VirtualMemorySize64がプロセスの仮想メモリ量を返し、Virtual Bytes性能カウンターに対応することについて。 ↩ -
Microsoft Sysinternals, VMMap. プロセスのコミット済み仮想メモリを種類別に分解し、それぞれへ割り当てられた物理メモリ(Working Set)と詳細なメモリマップを表示する機能について。 ↩ ↩2
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
Process Explorer / Handle / VMMap実践 ── ハング・リーク・「ファイルが使用中」を今この瞬間の状態から追う
「だんだん重くなる」「ファイルが消せない」「ハングした」を調べるSysinternals実践第2弾です。Process Explorerのハンドル検索・スレッドスタック・リーク監視カラム、handle.exe、VMMapでのメモリ内訳の切り分けを実務目線で解説します。
WMI/CIMをC#・PowerShellから使う ── ハードウェア情報取得・プロセス監視・リモート照会の実務ガイド
PCのシリアル番号取得、ディスク空き監視、プロセス起動検知の定番がWMI/CIMです。Get-CimInstance等のCIMコマンドレットの使い方と旧Get-WmiObjectからの移行、C#のSystem.ManagementとCIM APIの使い分け、実例レシピと落と...
Get-WinEventでイベントログを実務的に調べる ── 絞り込みの速さが調査時間を決める
Windowsのイベントログ調査をPowerShellで効率化する方法をまとめます。Where-Objectで絞ると遅い理由、FilterHashtableとXPathの使い分け、再起動やログオン・アプリ異常終了の調査レシピ、複数台からの収集までを解説します。
Windowsの時刻同期(w32time)と業務システム ── 「ログのタイムスタンプが合わない」を仕組みから解決する
装置とPCでログの時刻がずれる原因をWindows Timeサービス(w32time)の仕組みから解説。ドメイン階層とワークグループの既定動作、w32tmコマンドでの診断、高精度時刻や仮想マシンの注意点、StopwatchとUTC併用のログ設計まで整理します。
Process Monitor(ProcMon)実践ガイド ── 「設定が読まれない」「ACCESS DENIED」を10分で特定する
「設定を直したのに反映されない」などの不具合は、Process Monitor(ProcMon)でファイル・レジストリアクセスの実態から原因を特定できます。フィルタの実務的な使い方、NAME NOT FOUND等の読み方まで解説します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
業務アプリ、装置連携、通信ツールなどの Windows ソフト開発を支援します。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- タスクマネージャーの「メモリ」は、アプリが確保している全メモリですか?
- いいえ。タスクマネージャーには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、ディスク遅延、処理時間を同じ時間軸で確認してください。