目には見えない .NET のメモリアロケーションコスト

Simple Talk に興味深い記事が掲載されていた。Andrew Hunter 氏が書いた “Object Overhead: The Hidden .NET Memory Allocation Cost”。以下はその和訳。

オブジェクトの保持には思いのほか多くのメモリが使われます (32 ビットか 64 ビットかによって違いますが)。32 ビットのシステムでは、各オブジェクトは 8 バイドのオブジェクトヘッダーを持ち、さらにどこかからの参照 (4 バイトのポインタ) が必要です。つまり、単純にオブジェクトが存在するだけで 12 バイト必要になります。

64 ビットだと状況はもっと悪くなります。16 バイトのオブジェクトヘッダーに 8 バイトのポインタ。オブジェクトが存在するためだけに 24 バイト必要です。小さなオブジェクトを大量に持っているようなアプリケーションの場合、32 ビットから 64 ビットへの切り替えはひどい状況をもたらすことでしょう。メモリ不足ということにはならないかもしれませんが、要求されるリソースは 3 倍に増えてしまいます (訳注:12 バイトから 24 バイトなんだから 2 倍では?)。

このオーバーヘッドによって .NET アプリケーションにオブジェクト数の制限が課されます。32 ビットシステムではオブジェクトが存在するためだけに 12 バイト必要であり、これは最大オブジェクト数は 1 億 7000 万くらいであることを意味します。しかし、これでは多くのオブジェクトは無価値です。これらのオブジェクトにはデータを持っていないのですから。4 バイトのデータを持たせると、最大オブジェクト数は 1 億 3000 万に減ります。この時、アプリケーションによって使用されているメモリの 75% はオーバーヘッド分であり、非常に非効率的です。

ここで、望ましい効率について考えてみます。.NET インフラによって消費されるメモリ量は、オブジェクトの数と平均サイズから算出することができます。例えば、オーバーヘッドを 10% にするためには、32 ビットシステム上のオブジェクトは平均 80 バイトのデータを保持しなければなりません(この時の合計サイズは 88 バイト。8 バイトのヘッダーは全体の 10% を占めることになります)。この場合、オブジェクトの最大数は 2400 万です。64 ビットシステムで同様の効率を実現するには、オブジェクトは 160 バイト程度のデータを持つ必要があります。

これを成し遂げるのは難しいように思えます。80 バイトというとオブジェクトは 10 個のフィールドを持たないといけないことを意味しますが、このように大量のフィールドを持つことは通常よくないこと(bad code smell)であり、クラスが複雑化しているのでリファクタリングの必要ありだと考えられています。しかし、シンプルで実用的な解決策があります。アプリケーションが必要とする大規模データストレージ (訳注:DataSet のようなインメモリ DB のことを指しているのかな?) が値型の配列となるように、アプリケーションを設計するのです。配列のオーバーヘッドはそのサイズに関わらず一定ですし、値型はボクシングさえされなければオーバーヘッドを持ちません。配列へのアクセスの詳細は、配列のインスタンスを保持し、データアクセスのための抽象的なインターフェースを提供するクラスを作って隠蔽することができます。

ですが、このやり方は新たな非効率的な状況、ヒープの断片化をもたらします。何度も配列の作成・削除、もしくはリサイズを繰り返すと、インスタンスの作成とガベージコレクションの繰り返しによってメモリ内に穴ができてしまいます。こうなると、例えアプリケーションにメモリリークの問題がなくても徐々にメモリ不足に陥っていくことになります。私は以前に書いた記事で、この問題といくつかの回避策について取り上げました。

CLR によって生成された全てのオブジェクトは目に見えないメモリコストを免れません。大規模インメモリデータストレージにとって、無数の小さなオブジェクトはコストを許容できないレベルまで引き上げます。特に 64 ビットシステムでは。ある時点でメモリ上に存在するオブジェクトの数を減らすことが、.NET アプリケーションのキャパシティと効率性を上げるための効果的な方法です。

メモリ使用効率を考えると、結構な無駄があるらしい。今までオブジェクトヘッダーの消費メモリなんて全く考えたことがなかった。TechEd松崎氏のセッションで聞いた 32 ビット→64 ビットでメモリアラインメントが変わる云々の話を聞いたときも思ったが、.NET を使っているとメモリ管理についてあまり意識しないので、実は無駄をやっているっていう状況に陥ってしまう危険性がある。こういう目に見えないところで何が行われているかも、意識的に勉強しておいた方がよさそうだ。

Comments

Popular posts from this blog

WPF の RichTextBox に文字列を設定する&取り出す

WPFアプリにアニメーションGIFを表示させる

TFS: 別PCでのチェックアウトを取り消す