トップ回答者
最大仮想メモリ使用量(プライベート分)の測定

質問
-
初めて投稿させていただきます。自前アプリの最大の仮想メモリ使用量(プライベート分)を、そのアプリ内で計
測したいと考えております。Process Explorer(www.sysinternals.com)が Peak Private Bytes として表示
している数値のことです。これを VC++ で実装するにはどのようにすればよいのでしょうか?何かヒントを
いただけると助かります。Performance Data Helper Library(PDH) というものがありますが、この目的と
関連する情報が検索では見つからなかったのと、PerfMon.msc(PDH を使ってい
ると理解しています)でもこの値を見ることができないということは PDH では
無理なのではという想像より、深く調べておりません。以下は、これまでに試したことです。
DDK を導入し、NtQueryInformationProcess に VM_COUNTERS_EX 構造体のアドレ
スを渡すことで、他の観点のメモリ使用量は計測することが出来ました。そこで、VM_COUNTERS_EX.PeakVirtualSize -
(VM_COUNTERS_EX.VirtualSize - VM_COUNTERS_EX.PrivateUsage)により(括弧内はシェア分と考えています)、目的とする値を概算しようと考え
ましたが、うまくいきませんでした。Process Explorer の示す値と近いことも
ありましたが、負の値になることすらありました。プライベートの使用量がピー
クになった時点でのシェア分が、NtQueryInformationProcess 呼び出し時点のシェ
ア分とほぼ同じという保証などないので当然だと考えております。
よろしくお願いいたします。開発環境:
Windows XP Professional SP2、Visual Studio 2005 Professional、DDK 3790.1830
アプリ動作環境:
Windows 2000/XP Professional/XP Professional x64 Ed.
回答
-
> Process Explorer(www.sysinternals.com)が Peak Private Bytes として表示
している数値のことです。psapi の GetProcessMemoryInfo で得られる情報では駄目なのでしょうか?
Process Explorer と適当に書いたサンプルを見比べてみると、PROCESS_MEMORY_COUNTERS のPagefileUsage が Private Bytes に、PeakPagefileUsage が Peak Private Bytes に対応しているように見えます。
Process Explorer 自身は psapi.dll を利用していないようですから、一致しない可能性もあります。そのあたりはきちんと確認してみて下さい。
すべての返信
-
> Process Explorer(www.sysinternals.com)が Peak Private Bytes として表示
している数値のことです。psapi の GetProcessMemoryInfo で得られる情報では駄目なのでしょうか?
Process Explorer と適当に書いたサンプルを見比べてみると、PROCESS_MEMORY_COUNTERS のPagefileUsage が Private Bytes に、PeakPagefileUsage が Peak Private Bytes に対応しているように見えます。
Process Explorer 自身は psapi.dll を利用していないようですから、一致しない可能性もあります。そのあたりはきちんと確認してみて下さい。
-
SAKAMOTO さん、コメントありがとうございました。
お返事が遅くなり申し訳ありません。ご指摘の通り、PROCESS_MEMORY_COUNTERS の PeakPagefileUsage でいけそうです。また、同名のメンバが VM_COUNTERS にもありますが、この値も全く同じになることを確認しました。Process Explorer はVM_COUNTERS を使っているのかもしれません。
PeakPagefileUsage というメンバがあるのは知っていたのですが、「ページアウトされたことのあるページだけを対象にしたものだろう」と考えてしまい、着目しておりませんでした。PeakVirtualSize や PrivateUsage に注目し過ぎていました。
上の考え方は間違っていて、『ページファイル空間は、ページが初めて作成されるときに予約されますが、…』(インサイド Microsoft Windows 第 4 版 上 の 7.6.5 より引用)でいうところの予約がなされた段階で、PagefileUsage や PeakPagefileUsage にはカウントされる、というのが正しいと今は思っております。
その裏づけために、char* p = new char[1000000]; などとした直後に、WorkingSetSize が増える場合でも、PeakPagefileUsage もほぼ同じだけ増えることを確かめました。余談になりますが、VC++ 2005 で作ったコンソールアプリで、Debug ビルドでは WorkingSetSize は増えましたが、Release ビルドでは増えませんでした(アクセスしてはじめて増えました)。
ありがとうございました。
-
何度もすみません。
「char* p = new char[1000000]; などとした直後に、WorkingSetSize が増える」のは、Debug ビルドでしたが、これは単に、Debug ビルドでは new の中で各バイトへの 0xcd の書き込みが起こっているからですね。Debug 版では new の最中にページフォールトが発生していました。
私は、コミットしただけで RAM が使用される(はじめてアクセスされるときにページフォールトが発生しない)状況を発生させようとしていたわけですが、それはありえないのでしょうか?48*1024 バイトという小さな領域を VirtualAlloc でコミットしたような場合ても、アクセスしてはじめて、ページフォールトを起こしながら RAM 使用量が増えていました(全バイトへのアクセスで、ページフォルトが 12 回、RAM 使用量が 48 KB 増加)。
-
> はじめてアクセスされるときにページフォールトが発生しない状況発生させようとしていた
この状況を実現するには、VirtualLock を利用するのが良さそうです。
VirtualLock の説明には、「The VirtualLock function locks the specified region of the process's virtual address space into physical memory, ensuring that subsequent access to the region will not incur a page fault.」と書かれています。
まず、VirtualAlloc でMEM_COMMIT してページを確保、次に VirtualLock してみると如何でしょうか?
(MEM_COMMITしていないといけないのは、VirtualLockの制約です。このあたりのこともヘルプに書かれています)
-
コメントありがとうございます。
私もヘルプを見たところ、VirtualAlloc のヘルプの"コミット済み"状態の説明のところに、「このページの読み取りまたは書き込みを最初に試みた場合にのみ、システムは各コミット済みページを初期化し、物理メモリへロードします。」と書いてありました。MEM_PHYSICAL というフラグもあり、Address Windowing Extentions のときにのみ指定できるようです。
そこで、
> まず、VirtualAlloc でMEM_COMMIT してページを確保、次に VirtualLock してみると如何でしょうか?
についてですが、初回アクセス時にはページフォールトが発生しなくなったものの、VirtualLock 時にページフォールトが発生していました。これはヘルプ通りの挙動といえそうです。
話を元に戻してまとめますと、少なくとも AWE を使わないなら、「PagefileUsage や PeakPagefileUsage が、コミット時に RAM に割り当てられ、ページアウトされたことのないページ分を除外してカウントするのでは」という心配は不要だと、間接的ながらいいきれたと思います。そういうカウントは意味がなさそうなので、可能性はそもそも低かったのですが…
ありがとうございました。