タスク・マネージャ→プロセス→メモリ(プライベートワーキング セット)の値

Windowsタスク・マネージャ→プロセス→メモリ(プライベートワーキング セット)で表示される 値なんですが、 これって、OSがプロセスに対して確保してある(物理)メモリのサイズが表示されてるようで、 アプリ側がプログラム的に現在確保しているメモリ量が表示されているわけじゃないのですね。 (面倒がって文献当たってないので嘘つきかもですが)

どうもメモリ確保の仕方(サイズ)によってはアプリ側がAPIでメモリー返していても いくらかプロセスに割り当てられたままになっているぽい...

以下のようなルーチンでちょっと試してみました。

 #include <windows.h>
 #include <stdio.h>
 
 template<int UNITSZ, int NUM> void test()
 {
     char buf[1024];
     sprintf(buf, "メモリ確保前\n", UNITSZ, NUM);
     MessageBox( NULL, buf, "test", MB_OK | MB_ICONINFORMATION );
 
     HGLOBAL hMem[NUM];
     for (unsigned i = 0; i < NUM; ++i) {
         hMem[i] = ::GlobalAlloc(0, UNITSZ);
         if (hMem[i])
             memset(hMem[i], 0, UNITSZ);
     }
     sprintf(buf, "メモリ %d * %d を確保しました\n", UNITSZ, NUM);
     MessageBox( NULL, buf, "test", MB_OK | MB_ICONINFORMATION );
 
     for (unsigned i = 0; i < NUM; ++i)
         ::GlobalFree(hMem[i]);
     sprintf(buf, "メモリ %d * %d を開放しました\n", UNITSZ, NUM);
     MessageBox( NULL, buf, "test", MB_OK | MB_ICONINFORMATION );
 
     HINSTANCE hLib = ::LoadLibrary( "kernel32.dll" );
     if (hLib) {
         typedef BOOL (WINAPI* LPFN)(HANDLE, SIZE_T, SIZE_T);
         LPFN fnSetProcessWorkingSetSize
                = (LPFN)::GetProcAddress( hLib, "SetProcessWorkingSetSize" );
         if ( fnSetProcessWorkingSetSize )
             fnSetProcessWorkingSetSize(::GetCurrentProcess()
                                        , 0xFFFFFFFF, 0xFFFFFFFF );
         ::FreeLibrary( hLib );
     }
     sprintf(buf, "プロセスの持っているメモリを最小化しました\n");
     MessageBox( NULL, buf, "test", MB_OK | MB_ICONINFORMATION );
 }
 
 int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
 {
     test<8192, 64*1024>();
     test<64*1024, 8192>();
     test<0x100000, 512>();
     test<512*0x100000, 1>();
     return 0;
 }

512MBのメモリを8K,64K,1M,512Mバイト単位で確保開放してみる(その要所ごとにダイアログ表示)だけの処理をして、 ダイアログ表示ごとに、Windowsタスク・マネージャのプロセス・メモリの値を目視で控えてみたところ

メモリ 確保前 確保後 解放後 最小化後
8KB*64K個1.612MB528.768MB44.608MB1.280MB
64KB*8K個1.596MB532.820MB44.788MB1.348MB
1MB*512個2.332MB529.824MB2.404MB1.344MB
512MB*1個1.600MB526.920MB1.608MB1.340MB

といった感じでした。(環境はvista64(8GB)にてvs2008上でデバッグコンパイルでの実行)

この値は一例で、実行のたびに、ささやかな状況の違い(ウィンドウ操作の具合とか)のためか 数百K~数MBくらい値はずれるので、こまかな値は気にしないでください、なんですが、 それでも8K,64K単位での解放後の値が、余分に40数MB確保されたままになっているのが、 キモでしょうか。
そして、メモリー最小化の処理でばっさり開放されることも。

最小化の処理ってのは、OSがメモリー不足のおり、行う処理のひとつ(だったと思う)なんで、 メモリー不足になるまでは、少々プロセスが過剰にメモリーをガめているようにみえても、 不足時に開放されるのだから、最悪の事態ってわけじゃないようなあ、と思うのです。

ただ、メモリー不足ぎみの時に処理が適切に行われないと、場合によっちゃ、 (Aのプロセスに開放可能なメモリが残ってるのにBのプロセスの一部がスワップアウトとか) とか体感をそこねる状態もあるかもしれない(?ないかもだけど)ので、 OSがどの程度ちゃっかりしてくれてるか、にかかっているのですが、 このへんの具合がさっぱり...(教えて詳しい人、状態)

ちゃんと調べりゃわかるのかもだけど、わからない状態では、 アプリ側でメモリー最小化を適度にやるのがベター、という選択もありだろうで、 ただ、メモリー最小化を行う SetProcessWorkingSetSize は仮想メモリがらみのAPIで 場合によっちゃスワップアウトを起こすのかよくわからず(最小化のための使い方なら大丈夫?)だけど、 動作内容を思うと、そう頻度よく使えるような代物でもないだろうで、これのサジ加減も...

使いすぎて、 見た目のメモリー使用量はへってカタログスペック的にはよくなっているけれど、実は実行効率は悪くなっている、 という事態も考えらるわけで。

まあ、ie(コンポーネント)でflashプラグイン側で(開放可能な)メモリがどんどん確保 されっぱなしになっていくのは、やっぱりマズイだろうという気はするので (そもそもメモリ最小化で開放可能なメモリが再利用されてなさそうなのが問題なような)、 undonutでの対処はそう悪くないと思うのですが、 flashのような大量にメモリ使うページを見ない人にとっては、余計なことかもなあ、という気もします。

  2008-11-03


追記:思えばwin-apiのアロケート(GlobalAlloc,HeapAlloc)が他の環境(os)での mallocライブラリと同様なことやってるんでしょうね。mallocの中に入っていくと 管理らしいものは見当たらずHeapAllocに行き着くし... GlobalAllocは違うと 思いこんでたけど、上記の結果をすればその類でしょうか? (それならアプリで開放してもアロケート処理自体が保持しててもおかしくなく). vcでプログラムしていると、freeした領域が無効メモリになっていたりしてるので、 割とこまめにosにメモリー返してるような印象はあるんですが.(アクセス制限だけ?) 検索すると

http://d.hatena.ne.jp/NyaRuRu/20080414/p1

に解説ありますね。

アドレス空間消費の具体的なことはわかりませんが..

2008-12-23


Last-modified: 2008-12-23