.NETアプリケーションとワーキングセット

id:NyaRuRuさんの一連のWindowsのメモリ管理に関する話題は非常に興味深く拝見させて頂きました。

関係するエントリ

http://d.hatena.ne.jp/NyaRuRu/20051021
http://d.hatena.ne.jp/NyaRuRu/20051022

この中のNyaRuRuさんが書いていた以下の一文を読んでいて

id:NyaRuRu:20050607:p1 でも紹介したように,ウィンドウ最小化すると "Working Set" が減少しています.これは,ウィンドウ操作に関するヒューリスティックに基づいた最適化と言えるでしょう.

以前、.NETアプリケーションのワーキングセットに関して調べていたのですが不思議に思ったことがあったのを思い出しました。
当時、DelphiC++Builder等でビルドしたWin32ネィティブのバイナリ(exe)に比べて、.NETで開発したアプリケーションのワーキングセットに非常に大きな値が使用されていることに不安を覚えたことがあります。そこで私はまず「取り敢えず見かけ上のメモリ消費量を減らそう」と思い、てっとり早くワーキングセットの値を減らすことはできないだろうかとベタなことを考えてまずは以下のコードを書きました。

System.Diagnostics.Process curProcess = System.Diagnostics.Process.GetCurrentProcess();
curProcess.MaxWorkingSet = (IntPtr)期待するワーキングセットの最大値;
curProcess.MinWorkingSet = (IntPtr)期待するワーキングセットの最小値;

.NETは適切なセキュリティパーミションがあればこのようにカレントプロセスを取得して、ワーキングセットのサイズを変更することができます。ところがこのようなコードでいろいろな値を試しているうちにこの二つのプロパティにセットする値によりワーキングセットが不思議な振る舞いをしていることに気がついたのです。

System.Diagnostics.Process curProcess = System.Diagnostics.Process.GetCurrentProcess();
curProcess.MaxWorkingSet = (IntPtr)((int)curProcess.MaxWorkingSet - 1);
curProcess.MinWorkingSet = (IntPtr)((int)curProcess.MinWorkingSet - 1);

コードだけを見ると単にワーキングセットの上限と下限を現在の値から1減じた値にしているだけですが、このコードをWindowsFormsアプリケーションの実行時に呼ぶと実際のワーキングセットは現在の値-1どころか、極端に減る(4,50MB->4MB程度)のがタスクマネジャ等で見て取れるのです。これはまるで必要最小限のワーキングセットを計算してセットしているように見える振る舞いなのですがこの動作もNyaRuRuさんが書いたようにO/Sのメモリ管理によるヒューリスティックな最適化動作なのか? ということです。

と思ったら当時にはまだアップされていなかったKB293215に以下のような記述があるじゃあないですか。

A process can explicitly trim its own working set by calling the SetProcessWorkingSetSize() API while passing "-1" for both the dwMinimumWorkingSetSize and dwMaximumWorkingSetSize parameters. This is essentially how the system trims the process when its top-level window is minimized. This does not mean that the memory pages used by the process are immediately discarded from RAM. In fact, these pages may remain resident for quite a while. They are simply flagged so that the system can use them for other processes as necessary. This is significantly faster than waiting on the system's standard trimming algorithm.

やはりWindows側が勝手に行っていることのようですね。

最後に、.NETアプリケーションとしてはワーキングセットは最初に予約されたかなり大きめの値が良いのか、それともO/Sや他のアプリケーションのことを考えてできるだけ小さいワーキングセットのほうが良いのか、どちらが最適なのでしょうか。

1GByte以上、等潤沢にメモリを搭載している環境と256MByte以下のメモリ量しかない環境の場合同じように考えることはできないような気がします。また、対象がASP.NETSQLServerのように常駐し続けるサーバアプリケーションなのか、WindowsFormsのように必要に応じて起動、停止するクライアントアプリケーションなのかによっても最適なワーキングセットは変ってくると考えているのですが実際のところはどうなのでしょう。

この辺は.NETアプリケーションのメモリチューニング的な部分になると思うのですが、私が勉強不足なのか、情報が不足なのか適切なメモリ使用方針がまだ見つからない状況です。

それほど神経質になる必要は無いのかもしれません、がしかしアプリケーションを実際に使うユーザである程度PCの知識があるユーザは、タスクマネジャで見た時に保持するメモリが多いと見るとこれを非常に嫌悪する傾向にあります。従ってワーキングセットを多く設定する場合はきちんとその理由を説明できないと問題になることがあるので疎かにはできないのです。