最近学んだように、ガベージコレクターの仕組みを知っている人は多くありません。 開発者の99%が実際にこれを必要としないことは理解していますが、.NETでアプリケーションを開発するすべての人にその仕組みを知ってもらいたいと思います。 この記事では、ガベージコレクターが実際にどのように機能するかを簡単に説明します。
オブジェクトの寿命に関する基本情報
ご存じのように、メモリ内のオブジェクトを初期化するとき、オブジェクトに必要なスペースを割り当てます。 newキーワードを使用すると、いわゆるマネージヒープにクラスオブジェクトが追加され、オブジェクトへの参照が返されます。
C#でアプリケーションを作成する場合、.NETランタイムがプログラマによる直接の介入なしにコントロールヒープ自体を処理すると想定しても安全です。 実際、メモリ管理の「
新しいキーワードを使用してオブジェクトをコントロールヒープに配置し、それを忘れます。
作成後、不要になったオブジェクトはガベージコレクターによって自動的に削除されます。 オブジェクトが不要になったとき、ガベージコレクターがどのように決定するかという疑問がすぐに生じますか?
簡単な例を見てみましょう:
public static void MakeACar() { // myCar // Car, // Car ** . Car myCar = new Car(); ... }
Car(myCar)オブジェクトへの参照は、MakeACar()メソッド内で直接作成され、定義スコープ外に渡されなかったことに注意してください(戻り値としても、ref / outパラメーターとしても)。 したがって、メソッドを呼び出した後、myCarへのリンクは到達不能になり、Carオブジェクトはガベージコレクターによる削除の候補になります。 ただし、MakeACar()メソッドが実行された直後にオブジェクトが削除されるという保証はないことを理解しておく必要があります。 現時点で想定できることは、次にCLR環境でガベージコレクションが実行されるときに、myCarオブジェクトが削除用に設定されることだけです。
C ++プログラマーは、ヒープ上のオブジェクトの削除を特に管理しない限り、 「メモリリーク」がすぐに発生し始めることをよく知っています。 実際、メモリリークの問題を追跡することは、管理されていない環境でのプログラミングの最も退屈で時間のかかる側面の1つです。
アプリケーションルート要素の役割
オブジェクトが不要になったときにガベージコレクターがどのように決定されるかを理解するには、 アプリケーションのルート要素が何であるかを知る必要があります。 簡単に言えば、 ルート要素は、ヒープ上にあるオブジェクトへの参照を含むメモリ内のセルです。 厳密に言えば、要素はルートと呼ぶことができます:
- グローバルオブジェクトへの参照(C#では許可されていませんが、CILコードを使用するとグローバルオブジェクトを配置できます)
- 静的オブジェクトまたは静的フィールドへのリンク。
- アプリケーションコードベース内のローカルオブジェクトへのリンク。
- メソッドに渡されるオブジェクトパラメータへのリンク。
- ファイナライズを待っているオブジェクトへのリンク。
- オブジェクトを参照するCPUレジスタ。
ガベージコレクションプロセス中に、ランタイムはヒープ上のオブジェクトを調べて、アプリケーションにまだ到達可能かどうか(つまりルート)を判断します。 これを行うために、CLRは、アプリケーションが到達可能なすべてのオブジェクトを表すオブジェクトグラフを作成します。 さらに、ガベージコレクターは同じオブジェクトのグラフを2回作成しないため、COM環境でのプログラミングでは一般的な循環参照カウントを実行する必要がないことに注意してください。
オブジェクトの世代
到達不能コードを検出しようとすると、CLRオブジェクトはヒープ上のすべてのオブジェクトを文字通りスキャンしません 。 明らかに、これには、特に大規模なプロジェクトでは多くの時間がかかります。
プロセスを最適化するために、ヒープ上の各オブジェクトは特定の「世代」に属します。世代のアプリケーションでの意味は非常に単純に見えます。
オブジェクトがヒープ上にある時間が長いほど、オブジェクトがヒープに残る可能性が高くなります。
たとえば、デスクトップアプリケーションのメインウィンドウで定義されたクラスは、プログラムが終了するまでメモリに残ります。 一方、ごく最近束になったオブジェクト(たとえば、メソッドの範囲内にあるオブジェクト)は、ほとんどすぐに到達不能になります。 これらの仮定に基づいて、ヒープ上の各オブジェクトは以下を参照します。
- ジェネレーション0。ガベージコレクション中に削除に適したものとしてマークされたことがない、新しく新しく配置されたオブジェクトが識別されます。
- 第1世代。1つのガベージコレクションプロセスを既に「生き残った」オブジェクトを識別します(削除に適しているとマークされましたが、ヒープの十分な空き領域が原因で削除されませんでした)。
- 世代2。複数のガベージコレクションの実行を生き残ったエンティティを識別します。
ジェネレーション0と1はエフェメラルと呼ばれます。
ガベージコレクターは、最初に世代0に属するすべてのオブジェクトを分析します。削除後に十分なメモリが残っている場合、残っているすべてのオブジェクトのステータスは世代1に上がります。世代0のすべてのオブジェクトがチェックされたが、追加のスペースが必要な場合は、チェックが行われますジェネレーション1のオブジェクト。生き残ったこのジェネレーションのオブジェクトはジェネレーション2のオブジェクトになります。ガベージコレクタがまだメモリを必要とする場合、ジェネレーション2のオブジェクトはガベージコレクションを受けます。 第2世代以上のオブジェクトはないため、オブジェクトのステータスは変更されません。
上記から、新しいオブジェクトは古いオブジェクトよりも速く削除されると結論付けることができます。
.NET 1.0-.NET 3.5の並列ガベージコレクション
.NET 4.0のリリース前は、未使用のオブジェクトはパラレルガベージコレクション技術を使用してクリーンアップされていました。 このモデルでは、 一時オブジェクトでガベージコレクションを実行している間、ガベージコレクターは現在のプロセス内のすべてのアクティブスレッドを一時的に停止して、ガベージコレクションプロセスが完了するまでアプリケーションが管理ヒープにアクセスできないようにしました。
ガベージコレクションサイクルの終わりに、中断されたスレッドは作業を再開できました。 幸いなことに、.NET 3.5では、ガベージコレクタが適切に最適化されていたため、それに関連付けられたアプリケーションでの作業の短い中断はめったに目立たなくなりました。
最適化と同様に、パラレルガベージコレクションにより、別のストリームで一時的な世代のいずれでも検出されなかったオブジェクトをクリーニングできました。 これにより、.NETランタイムがアクティブスレッドを一時停止する必要性が減りました(排除はされませんでした)。 さらに、パラレルガベージコレクションにより、非一時的な生成オブジェクトのアセンブリ中にオブジェクトをヒープに配置できました。
.NET 4.0のバックグラウンドガベージコレクション
そして最後に、.NET 4.0のガベージコレクターの改善についてお話ししたいと思います。
.NET 4.0では、ガベージコレクタは、 バックグラウンドガベージコレクション技術を使用して、スレッドの一時停止と管理ヒープ内のオブジェクトのクリーニングの問題を別々に解決します。 名前にもかかわらず、これはすべてのガベージコレクションが追加のバックグラウンドスレッドで行われることを意味するものではありません。 実際、非一時生成オブジェクトのバックグラウンドガベージコレクションの場合、.NETランタイムは、別のバックグラウンドスレッドで一時生成オブジェクトをガベージコレクションできるようになりました。
.NET 4.0のガベージコレクションメカニズムが改善され、ガベージコレクションパーツに関連付けられたストリームを一時停止する時間が短縮されました。 これらの変更により、世代0および1の未使用オブジェクトのクリーニングプロセスが最適化されました。 これにより、より高いレベルのアプリケーションパフォーマンスを得ることができます。
おわりに
結論として、.NET 4.0で使用されるガベージコレクターは、プログラムの動作を最適化でき、実際にはパフォーマンスに影響を与えないと言いたいと思います。
次の記事では、System.GC名前空間を使用してガベージコレクションプロセスを個人的に管理する方法を説明します。
詳細: こちら