none
プロセスのメモリ使用量のうちページアウトサイズを知りたい RRS feed

  • 質問

  • 【背景】
    アプリの使用メモリ量を取得したいと思い、色々ツールやAPIを調査したところ、
    仮想メモリサイズ(ProcessExplorerで言うPrivate Bytes)、ワーキングセット、
    Vistaのタスクマネージャの「メモリ(プライベートワーキングセット)」などを組み合わせたところで、
    「ページアウトされたせいなのかnew(malloc)したけども使っていないせいなのか」を判別出来なさそうだと思いました。
    しかし、この差は一般的にはハードページフォルトが発生するかどうかがかなり違うと思い、
    出来れば識別したいと考えました。(ある意味OS側のワーキングセットの調整を信用していない。)

    【前提】
    対象のプロセスとはソースが未公開の他人の作ったプログラムです(例えばWebブラウザ)。
    話を簡単にするために、DLL、共有メモリ、メモリマップドファイルなどは使用していない前提とさせて下さい。
    (なぜ上記前提なのかは聞かないで下さい・・)

    ページフォルトの回数などをパフォーマンスモニタなどで時系列で増加タイミングを見るとほぼ分かると思いますが、
    あるタイミングを切りだした状態での判別方法を知りたいと考えています。
    (なぜこの前提なのかは聞(略)

    【実現のための手段として模索した内容】
    調査のためのツールとして、ProcessExplorer、タスクマネージャ、GetProcessMemoryInfo()、
    パフォーマンスカウンタ、windbg/ollydbgの各種出力内容とそれぞれの値がどれと一致しているか。
    インサイドWindowsの7章やNyaruruさんページなど。
    上記調査から「ページアウトされたせいなのかnew(malloc)したけども使っていないせいなのかを判別」する方法は
    以下があると考えました。

    【現在検討中の案】
    ①スワップされたサイズを得る方法
    OS全体ではなく、対象プロセスのみの値はパフォーマンスモニタにはない模様?
    となると、PTEから辿っていくしかなさそう。
    ②確保(commit済み)だけども使っていない領域は、
    デマンドゼロページの総数の模様(そうではないケースも多々ありそうですが)。
    となると、結局PTEから辿っていくしかなさそう。
    (OSやヒープマネージャなどで変わりそうですが)
    ③ページフォルトの種類の判別をプロセス単位で取得する方法があれば出来そう。
    でもなさそう。。pfmon.exeで分かるようだけれども、関数として提供されているのが望ましいです。

    上記の実現のためにカーネルモードのアクセスが必要でも仕方無いとも考えています><

    【知りたいこと】
    もっと簡単な方法がないでしょうか?(出来れば、一般アプリと同様の権限で対応出来る範囲内としたい)
    なんだか難しく考えすぎな気がしています。
    (windbgの!コマンドの動きを変えさせる方法はあったりしますでしょうか?)
    上記を実現するための、コードはどこを見れば参考になりそうでしょうか?
    2009年9月12日 16:29

すべての返信

  • 合ってるか自信ありませんが、書いてみます。

    まずプロセスが使っているメモリはPrivate Bytes(Commit SizeまたはVM Size)と呼ばれる。
    そのうち物理メモリが割り当てられているメモリはWorking Setと呼ばれる。
    従ってページアウトサイズはPrivate Bytes - Working Set。

    でダメですか? であればリソースモニタやタスクマネージャで見ることができます。APIとしてはGetProcessMemoryInfo になります。
    ちなみにGetProcessMemoryInfoからたどれますが、Process Memory Performance Information にまとめられています。
    2009年9月12日 22:38
  • ありがとうございます!(でもダメですね)
    用語もやっかいでしたね。

    私が、「仮想メモリサイズ」といったのは佐祐理さんの言う「Private Bytes(Commit SizeまたはVM Size)」に相当します。

    上記にもありますが、以下は全て「仮想メモリサイズ」です。
    ・タスクマネージャ:仮想メモリサイズ(日XP)、コミットサイズ(日Vista)、VM Size(米XP)、Commit Size(米Vista)
    ・ProcessExplorer:Private Bytes
    ・パフォーマンスモニタ:Private Bytes、Page File Bytes
    ・構造体:PROCESS_MEMORY_COUNTERS_EX.PagefileUsage

    それぞれ値が完全一致することは確認済みです。


    「仮想メモリサイズ」で取得出来る値の問題点は、new(malloc)で確保したけども実際は使っていない領域も
    カウントされてしまうことだと認識しています。
    (その是非はともかく、せめて除いた値が欲しいというのが意図です)
    これが解決したいものの一つで、そのための実現方法の案が上記に書いたものになります。

    「ワーキングセット」(英語版ではWorking Set、Mem Usage)で取得出来る値の問題点は、
    ページアウトすると減ってしまうことだと認識しています。
    (その是非はともかく、せめて減っていない値が欲しいというのが意図です)
    これが解決したいものの一つで、そのための実現方法の案が上記に書いたものになります。

    2009年9月13日 2:18
  • 「new(malloc)で確保したけども実際は使っていない領域」というのをもう少し詳しく説明してください。
    もしかしてVirtualAlloc()の予約済み領域のことですか? そうではなく使っている/使っていないがアプリ作成者の主観が基準ならば第三者に集計できるとは思えません…。
    2009年9月13日 14:06
  • 遅くなりました。

    >VirtualAlloc()の予約済み領域のことですか?
    いえ、VirtualAlloc(MEM_COMMIT)のつもりでした。(Cのunmanagedのリリースビルド版を想定していました)
    mallocはヒープマネージャによって動作がかなり変わるので、例としては適切ではありませんでした。
    OSは、たとえMEM_COMMITしても実際にメモリを与えるかは保留します。
    保留状況を見ることで、第三者で集計できると考えています。
    (予約サイズ(MEM_RESERV)はVirtualQuery等でサイズを正しく取得できるため、特に気にしません)

    >使っている/使っていないがアプリ作成者の主観が基準ならば第三者に集計できるとは思えません
    OSが判断した内容ということでよいと思っています。
    やりたいことは他人が作成したアプリのハードページフォルトのおきやすさの判断をこちらなりに評価したいためです。

    一部、カーネルモードでやらざる負えないのは腹をくくりました。

    memusage(PFNデータベースを見るようなもの)は見かけますが、
    あくまでプロセス単位でのページアウトサイズや、MEM_COMMITしたが物理メモリを確保するまでに至っていたいないもののサイズをしりたいため、
    ちょっと違うかなと。

    実はクラッシュダンプのサイズがそのまま私が欲しいものだったりするかもとも思っていたりします。
    (ただし、クラッシュダンプの出力ロジックがどうなってるか不明なので何とも言えませんが、
    OSが実際に物理メモリを確保したかどうかにかかわらずダンプしている気がしています。)

    # 英語版Forumと違いsysinternal専用掲示板のようなものがないようなので、ここで続けようかと。
    # ちょうどhttp://technet.microsoft.com/ja-jp/windows/ee424285.aspxの日本語訳も開始されましたし。
    2009年10月5日 14:25
  • MEM_COMMITしても物理メモリを割り当てていない状態と、ページアウトした状態の違いについてどうお考えですか。
    どちらも使われていないからページインしていないだけでは…?
    2009年10月8日 3:48
  • 少し長くなり恐縮ですが、

    基本的に、ページアウトはOS都合(システム都合)だと思っています。
    一方、MEM_COMMITしたけども使われていない領域はアプリケーションの都合だと考えています。
    アプリケーションからしてみれば、OS都合など知らないわけで、
    再度使う可能性が高いと思っています。
    もちろんOSの判断が正しい場合もあるとも思っています。

    OS側から見ると、ページアウトした領域とMEM_COMMITしたけども使われていない領域というものは
    ほとんど一緒のように見えるかもしれませんが、
    アプリケーションからしてみれば、
    1度も使われていない箇所を使い始めるかどうかより
    1度でも実際に使うために書き込んだり参照したりした領域(かつ開放されていない領域)を
    再度使う可能性の方が高い場合があると思っています。
    アプリケーションを起動してからどのような操作をしたのかにもよると思っています(実は一番重要)。
    それらをひっくるめて総合判断をしたいと考えているのです。

    様々な要素が考えるためメモリの使用状況だけではなく、
    OSの種類や対象アプリが過去にどのような動作をするケースが多かったかなど、
    最終的に総合判断を行うのですが、
    そのための前提となるより細かな情報が欲しいと考えています。

    MEM_COMMITしたけども使われていない領域は、
    アプリによって実はそれなりのサイズを占めている場合があると思っています。
    これらは対象となるアプリケーションを一通り操作させれば、
    以降の操作で増える可能性が低い場合があると考えています。

    実際、私個人のプログラムのテクニックとして
    最初に必要十分なサイズをあらかじめ用意しておき、
    前の方から順に使っていくケースはよくあることだと考えており、
    その場合に最後まで使うケースは時によってまれな場合があると思っています。
    そうすることで性能向上やソースコードの簡略化が出来るときがありました。

    Microsoftはこのような要望に対応するために
    プロセス単位でPeak WorkingSetというものを
    用意してもらっているとは思っていますが、
    それでは、不十分だと考えています。
    Peakになった時点で実際に対象アプリがどの程度のメモリが欲しかったかが、
    すぐには判断つかないためです。

    私は元々タスクマネージャで表示されるメモリ使用量というのは、
    ページアウトサイズも含んだ値かつ、MEM_COMMITしたけども使われていない領域は引いたサイズが
    出ているべきだと考えていました。
    ところが調べてみると実際はそうではありませんでした。
    それどころか、色々なAPIを組み合わせてみてもそれを知る方法はありませんでした。

    例えばブラウザというものは、ブラウザを提供する側、各プラグイン、Webページする側
    それぞれが相互に影響し合い、メモリを使用しているものと思っています。
    メモリが不正に増加している場合に、何が原因になっているのか突き止めにくいと考えています。
    そのための、手助けとなる情報を少しでも多く仕入れたいと思っています。

    ブラウザのみに対象を絞れば、これ以外にも沢山よい案があるとは思っていますが、
    ブラウザのみに対象を絞っているわけではありません。

    以上が、ページアウトサイズを知りたい動機になります。

    # そろそろ本題に入りたい。。。

    2009年10月8日 14:31
  • あと、一番大きな違いがあるのに気づきました。

    MEM_COMMITしてまだ使用していない領域を新たに使用する際は、
    物理メモリが空いていれば、物理メモリから割り当てられると考えています。
    ソフトページフォルトであるため、ディスクアクセスが発生するようなページインは行われないかと。
    タスクマネージャで見てみるとMEM_COMMITした段階で、コミットチャージ(PF使用量:ページファイル使用量)が増えますが、
    これはおそらく仮想的に割り当てたのみで、その後その領域にアクセスしたとしてもディスクアクセスは発生していません。
    (↑この「仮想的に割り当てたのみ」という記述が現在見つからず少し困っていますが)
    メモリが十分存在すれば、MEM_COMMITすること自体の動作も、実際にその領域にアクセスも
    両方高速に行われています。

    一方、ページアウトした領域に読み書きする際は、
    例えメモリが十分存在したとしても必ずディスクアクセスが発生してしまい、
    時間はそれなりにかかると考えています。

    2009年10月8日 23:34
  • 連休でほぼ完成してしまいました。
    2点疑問が発生しました。分かるかたいましたら教えてください。

    ①32bitPAEカーネルでの
    PDEとPTEの仮想アドレス空間の範囲が以下のように被っています。

    lkd> !pte C0603018
                   VA c0603000
    PDE at 00000000C0603018    PTE at 00000000C0603018
    contains 000000002629F863  contains 000000002629F863
    pfn 2629f      ---DA--KWEV    pfn 2629f      ---DA--KWEV

    この場合、PTEの内容が参照されるということでよいでしょうか?
    被っても平気である理由は、カーネル内部でPDE、PTEアドレスを参照する際は、
    物理アドレスを使用しているからという認識をしましたがどうでしょうか?
    (であればPDEやPPEを仮想アドレスにマッピングすることすら不要になるような?)


    ②無効PTE(Invalid PTE)の「デマンドゼロ」と「未知」の判別方法が分かりません。
    判別可能にするには「デマンドゼロ」は保護フラグ(Protection:5)内のいずれかのフラグが
    立ってないといけないと考えています。
    勝手な想定ですが、両者はそもそもいきなり仮想アドレスからPTEを参照した場合には、
    判別可能になっておらず(または保証されないか必要性がない状態)、
    カーネル内部の処理過程で一時的に判断可能な状態になるという認識をしました(なので本に書けない)。
    どなたか実際の所を分かる方いますでしょうか?

    • 回答の候補に設定 xyzyx 2014年5月13日 11:03
    2009年10月12日 13:34