自動メモリ管理

更新 : 2007 年 11 月

自動メモリ管理は、マネージ実行中に共通言語ランタイムが提供するサービスの 1 つです。共通言語ランタイムのガベージ コレクタは、アプリケーションが使用するメモリの割り当ておよび解放を管理します。したがって、開発者がマネージ アプリケーションを開発するときに、メモリ管理タスクを実行するためのコードを記述する必要はありません。自動メモリ管理により、オブジェクトを解放し忘れたためにメモリ リークが発生する、既に解放されているオブジェクトのメモリにアクセスしようとするなどの一般的な問題を回避できます。ここでは、ガベージ コレクタによるメモリの割り当て方法および解放方法について説明します。

メモリの割り当て

新しいプロセスが初期化されると、ランタイムは連続したアドレス空間領域をそのプロセスのために予約します。この予約済みのアドレス空間をマネージ ヒープと呼びます。マネージ ヒープは、ヒープ内で次のオブジェクトを割り当てるアドレスへのポインタを管理します。初期状態では、このポインタはマネージ ヒープのベース アドレスに設定されます。すべての参照型は、マネージ ヒープ上に割り当てられます。アプリケーションが最初の参照型を作成すると、マネージ ヒープのベース アドレスの位置にその型のメモリが割り当てられます。アプリケーションが次のオブジェクトを作成すると、ガベージ コレクタは、アドレス空間で最初のオブジェクトの直後のメモリをそのオブジェクトに割り当てます。ガベージ コレクタは、使用できるアドレス空間がある限り、この方法で新しいオブジェクトにアドレス空間を割り当てていきます。

マネージ ヒープからのメモリ割り当ては、アンマネージ メモリ割り当てよりも高速に処理されます。ランタイムはポインタに値を加算することによってオブジェクトにメモリを割り当てるため、この方法によるメモリ割り当ては、スタックからのメモリ割り当てとほとんど同じ速度で行われます。また、連続してメモリを割り当てられた複数の新規オブジェクトは、マネージ ヒープ内でも連続して格納されるため、アプリケーションからそれらのオブジェクトに高速でアクセスできます。

メモリの解放

ガベージ コレクタの最適化エンジンは、現在の割り当て状況に基づいて、ガベージ コレクションの実行に最適な時期を判断します。ガベージ コレクタは、ガベージ コレクションを実行するときに、アプリケーションが使用しなくなったオブジェクトのメモリを解放します。使用されなくなったオブジェクトを判断するために、アプリケーションのルートを調べます。すべてのアプリケーションには、ルートのセットがあります。各ルートは、マネージ ヒープ上のオブジェクトを参照しているか、または null に設定されています。アプリケーションのルートには、グローバル オブジェクト ポインタと静的オブジェクト ポインタ、スレッドのスタック上のローカル変数と参照オブジェクト パラメータ、CPU レジスタなどが含まれています。ガベージ コレクタは、ジャスト イン タイム (JIT) コンパイラとランタイムが管理している、アクティブなルートのリストにアクセスします。このリストを使用してアプリケーションのルートを調べ、ルートから到達できるすべてのオブジェクトを含むグラフを作成します。

このグラフに含まれないオブジェクトは、アプリケーションのルートからは到達できません。ガベージ コレクタは、そうした到達できないオブジェクトをガベージ (ごみ) であると判断し、それらのオブジェクトに割り当てられたメモリを解放します。ガベージ コレクション中に、ガベージ コレクタはマネージ ヒープを調べ、到達できないオブジェクトが占有しているアドレス空間ブロックを検索します。到達できないオブジェクトを検出すると、それらのオブジェクトに割り当てられていたアドレス空間ブロックを解放し、メモリ コピー機能を使用して、到達できるオブジェクトのメモリを圧縮します。到達できるオブジェクトのメモリを圧縮した後で、ガベージ コレクタは、アプリケーションのルートがそれらのオブジェクトの新しい位置を指すようにポインタを修正します。また、マネージ ヒープのポインタも、最後の到達できるオブジェクトの後を指すように修正します。メモリが圧縮されるのは、ガベージ コレクション中に、到達できないオブジェクトが一定数以上検出された場合だけです。マネージ ヒープ内のすべてのオブジェクトがごみではないと判断された場合は、メモリを圧縮する必要がありません。

パフォーマンスを向上させるために、ランタイムは、大きいオブジェクトのメモリは独立したヒープに割り当てます。ガベージ コレクタは、これらの大きいオブジェクトのメモリを自動的に解放します。ただし、メモリ内で大きいオブジェクトを移動するのを避けるため、このメモリは圧縮されません。

ジェネレーションとパフォーマンス

ガベージ コレクタのパフォーマンスを最適化するために、マネージ ヒープは 3 つのジェネレーション (0、1、および 2) に分類されます。ランタイムのガベージ コレクション アルゴリズムは、これまでのガベージ コレクション手法の利用経験から、コンピュータ ソフトウェア業界で有効だと認識されている次の原則に基づいています。まず、マネージ ヒープの一部のメモリを圧縮する方が、マネージ ヒープ全体のメモリを圧縮するよりも高速です。次に、オブジェクトが新しいほどその存続期間は短く、古いほど存続期間は長くなります。最後に、新しいオブジェクトは相互に関連を持つ傾向にあり、アプリケーションによって同時に頻繁にアクセスされます。

ランタイムのガベージ コレクタは、新しいオブジェクトをジェネレーション 0 に格納します。アプリケーションの有効期間の初期に作成され、ガベージ コレクションでごみではないと判断されたオブジェクトは昇格してジェネレーション 1 とジェネレーション 2 に格納されます。オブジェクトの昇格プロセスについては後で説明します。マネージ ヒープの一部を圧縮する方がヒープ全体を圧縮するよりも高速であるため、この手法では、ガベージ コレクタがコレクションを実行するたびにマネージ ヒープ全体のメモリを解放するのではなく、特定のジェネレーションのメモリだけを解放できるようにします。

実際には、ガベージ コレクタはジェネレーション 0 がいっぱいになったときにガベージ コレクションを実行します。ジェネレーション 0 がいっぱいになったときにアプリケーションが新しいオブジェクトを作成しようとすると、ガベージ コレクタは、そのオブジェクトに割り当てるためのアドレス空間がジェネレーション 0 には残っていないことを認識します。ガベージ コレクタは、ジェネレーション 0 のアドレス空間を解放して新しいオブジェクトに割り当てるために、ガベージ コレクションを実行します。まず、ガベージ コレクタは、マネージ ヒープ内の全オブジェクトではなく、ジェネレーション 0 のオブジェクトだけを調べます。一般に新しいオブジェクトの存続期間は短く、ジェネレーション 0 のオブジェクトの多くはガベージ コレクションが実行された時点でアプリケーションによって使用されていないと推測されるため、この方法は最も効率的です。また、多くの場合、ジェネレーション 0 のガベージ コレクションを行うだけで、アプリケーションが新しいオブジェクトの作成を続行するために十分なメモリを確保できます。

ガベージ コレクタは、ジェネレーション 0 のガベージ コレクションを実行した後で、このトピックの「メモリの解放」で既に説明したように、到達できるオブジェクトのメモリを圧縮します。次に、ガベージ コレクタはこれらのオブジェクトを昇格させ、それらのオブジェクトが占めているマネージ ヒープ内の部分をジェネレーション 1 と見なします。一般にガベージ コレクションでごみだと判断されなかったオジェクトの存続期間は長いので、これらのオブジェクトを上位のジェネレーションに昇格させるのは有効です。この結果、ガベージ コレクタがジェネレーション 0 のガベージ コレクションを実行するたびに、ジェネレーション 1 とジェネレーション 2 に昇格したオブジェクトを再び調べる必要がなくなります。

ジェネレーション 0 の最初のガベージ コレクションを実行し、到達できるオブジェクトをジェネレーション 1 に昇格させた後で、ガベージ コレクタはマネージ ヒープの残りの部分をジェネレーション 0 と見なします。ガベージ コレクタは、ジェネレーション 0 がいっぱいになってガベージ コレクションを再び実行する必要が生じるまで、ジェネレーション 0 のメモリを新しいオブジェクトに割り当てます。この時点で、ガベージ コレクタの最適化エンジンは、古いジェネレーションのオブジェクトを調べる必要があるかどうかを判断します。たとえば、ジェネレーション 0 のガベージ コレクションを行うだけでは、アプリケーションが新しいオブジェクトを作成するために必要なメモリを確保できない場合、ガベージ コレクタはジェネレーション 1、0 の順にガベージ コレクションを実行します。これでも必要なメモリを確保できない場合は、ジェネレーション 2、1、および 0 の順にガベージ コレクションを実行します。ガベージ コレクタは、それぞれのガベージ コレクション後にジェネレーション 0 内にある到達できるオブジェクトを圧縮し、それらのオブジェクトをジェネレーション 1 に昇格させます。ガベージ コレクションでごみだと判断されなかったジェネレーション 1 のオブジェクトは、ジェネレーション 2 に昇格します。ガベージ コレクタがサポートするジェネレーションは 3 つしかないため、ガベージ コレクションでごみだと判断されなかったジェネレーション 2 のオブジェクトは、その後のガベージ コレクションで到達できないオブジェクトであると判断されるまで、ジェネレーション 2 に残ります。

アンマネージ リソースのメモリの解放

アプリケーションで作成されるオブジェクトの大部分については、ガベージ コレクタによって、必要なメモリ管理タスクを自動的に実行できます。しかし、アンマネージ リソースでは、明示的なクリーンアップが必要です。最も一般的な種類のアンマネージ リソースは、ファイル ハンドル、ウィンドウ ハンドル、ネットワーク接続などのオペレーティング システム リソースをラップしたオブジェクトです。ガベージ コレクタは、アンマネージ リソースをカプセル化したマネージ オブジェクトの存続期間を追跡することはできますが、そのアンマネージ リソースのクリーンアップ方法については具体的な情報を持っていません。アンマネージ リソースをカプセル化するオブジェクトを作成する場合は、そのアンマネージ リソースをクリーンアップするために必要なコードをパブリックな Dispose メソッドという形で提供することをお勧めします。Dispose メソッドを提供すると、ユーザーがオブジェクトを使い終わったときに、そのオブジェクトのメモリを明示的に解放できます。アンマネージ リソースをカプセル化するオブジェクトを使用する場合は、Dispose メソッドの存在を念頭に置き、必要に応じて呼び出すようにしてください。アンマネージ リソースのクリーンアップの詳細と、Dispose を実装するためのデザイン パターンの例については、「ガベージ コレクション」を参照してください。

参照

処理手順

ガベージ コレクションの技術サンプル

概念

マネージ実行プロセス

参照

GC

その他の技術情報

ガベージ コレクション