メモリアロケーション

GDNJでちょろっと話題になっているが、.NET のメモリアロケーションは通常の Win32 アプリケーションと比較すると、微妙に異なる。*1
たとえば、パフォーマンスモニタを利用して Working Set Memory や Virtual Memory と、CLR Memory のカウンタを観察しながら WindowsForms アプリケーションでメモリを確保していってみよう。CLR Profilerなんかも一緒に動かしてもよいかもしれませんね。*2
さて、アプリケーションを起動して適当なオブジェクトを new したり、構造体を box 化していくと、順当に Working Set が増えるのに対し、Virtual Memory はその2〜5倍の速度で増加していくことがわかる。たとえば手元のテストアプリケーションでは、約6MBのメモリ確保によって 45MB ほどの Virtual Memory の増加が見られた。この増加分のほとんどは CLR Memory の Total reserved Bytes に割り当てられており、CLR はメモリアロケーションに対して仮想メモリの上限に達するまで、この reserved Bytes がガンガン増えていくようになっている。Large Object Heap に対してメモリ確保が発生しないような標準的なオブジェクトの生成を繰り返した場合、おおよそ 400-600MB の実メモリの確保が発生した頃には、ほとんどの Win32 環境での仮想メモリ確保上限である2GBに達する。*3この仮想メモリの使い方はかなり特徴的かつ富豪的である。*4
もう1つ、この CLR の確保した Managed Heap の特徴として、アプリケーションを最小化してみると、そのすべてが WorkingSet から除去されるということがある。これも恐ろしく豪快な動作ではないかと思うんだが、アプリケーションを最小化から元に戻してもメモリが一気に WorkingSet に戻ってくるわけではない。アプリケーションが実際に読み書きをして触ったところだけが WorkingSet へと戻ってくるようになっていて、いくつかの .NET アプリケーションで実際に最小化して元に戻した後のメモリの復帰状況をみていると結構おもしろい。


細かい所つっこんでも、つっこまれても疲れるので、とりあえず触りだけ…。

*1:問題になっている GDNJ の投稿は、/clr をはずしたとあるので、非 .NET アプリケーションの話題のようだ

*2:CLR Profiler アリとナシで2回にわけられないならナシで十分ですかね

*3:上限に達した状態で P/Invoke を呼び出して unmanged 側でメモリ確保に失敗してアプリケーションが異常終了する・・・なんてこともある

*4:表計算などで無制限に近い空間を提供する際、仮想メモリ上をリザーブしてピンポイントにコミットしていくという手法があるにはあるらしい