廃棄をガベージ コレクションから分離することが重要です。それらは完全に別個のものですが、1 つの共通点があります。これについては後で説明します。
Dispose
、ガベージ コレクションとファイナライズ
ステートメントを書くときusing
、それは単に try/finally ブロックのシンタックス シュガーであるため、ステートメントの本体のコードが例外をスローDispose
した場合でも呼び出されます。オブジェクトがブロックの最後でガベージ コレクションされるという意味ではusing
ありません。
廃棄は、管理されていないリソース(メモリ以外のリソース) に関するものです。これらは、UI ハンドル、ネットワーク接続、ファイル ハンドルなどである可能性があります。これらは限られたリソースであるため、通常はできるだけ早く解放する必要があります。IDisposable
タイプがアンマネージ リソースを直接 (通常は を介してIntPtr
) または間接的に (たとえば、Stream
、 aなどを介して) 「所有」する場合はいつでも実装する必要がありますSqlConnection
。
ガベージ コレクション自体はメモリに関するものにすぎません。ガベージ コレクターは、参照できなくなったオブジェクトを見つけて解放することができます。ただし、常にガベージを探すわけではありません - ガベージが必要であることを検出した場合のみです (たとえば、ヒープの 1 つの「世代」でメモリが不足した場合)。
ツイストはファイナライズです。ガベージ コレクターは、もはや到達可能ではないが、ファイナライザーを持っているオブジェクトのリストを保持します ( ~Foo()
C# のように記述されているため、やや紛らわしいですが、C++ のデストラクタとはまったく異なります)。メモリが解放される前に余分なクリーンアップを行う必要がある場合に備えて、これらのオブジェクトでファイナライザーを実行します。
ファイナライザーは、ほとんどの場合、型のユーザーが規則正しい方法で破棄するのを忘れた場合に、リソースをクリーンアップするために使用されます。したがって、 a を開いたときにorFileStream
を呼び出すのを忘れた場合、ファイナライザーは最終的に基になるファイル ハンドルを解放します。私の意見では、適切に作成されたプログラムでは、ファイナライザーが起動することはほとんどありません。Dispose
Close
変数の設定null
変数の設定に関する小さなポイントnull
- これは、ガベージ コレクションのために必要になることはほとんどありません。私の経験では、オブジェクトの「一部」が不要になることはめったにありませんが、メンバー変数の場合は時々それをやりたいと思うかもしれません。ローカル変数の場合、通常、JIT は (リリース モードで) 十分に賢く、いつ参照を使用しないかを認識します。例えば:
StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();
// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);
// These aren't helping at all!
x = null;
sb = null;
// Assume that x and sb aren't used here
ローカル変数を設定する価値があるのはnull
、ループ内にあり、ループのいくつかの分岐で変数を使用する必要があるが、使用しないポイントに到達したことがわかっている場合です。例えば:
SomeObject foo = new SomeObject();
for (int i=0; i < 100000; i++)
{
if (i == 5)
{
foo.DoSomething();
// We're not going to need it again, but the JIT
// wouldn't spot that
foo = null;
}
else
{
// Some other code
}
}
IDisposable/ファイナライザーの実装
では、独自の型でファイナライザーを実装する必要がありますか? ほぼ間違いありません。管理されていないリソースを間接的にのみ保持している場合 (たとえば、メンバー変数として を持っているFileStream
場合)、独自のファイナライザーを追加しても役に立ちません。オブジェクトがガベージ コレクションの場合、ストリームはほぼ確実にガベージ コレクションの対象になるため、単に依存することができます。FileStream
ファイナライザーを持つ (必要に応じて - 他の何かを参照する場合など)。管理されていないリソースを「ほぼ」直接保持したい場合SafeHandle
は、あなたの友人です-使い始めるのに少し時間がかかりますが、ファイナライザーを再度作成する必要はほとんど ないことを意味します. 通常、リソース ( ) を実際に直接処理する場合にのみファイナライザーが必要であり、IntPtr
SafeHandle
できるだけ早くすることができますように。(そこには 2 つのリンクがあります - 理想的には両方を読んでください。)
Joe Duffy は、ファイナライザーと IDisposable (多くの賢い人々と共同で作成) に関する非常に長い一連のガイドラインを持っており、読む価値があります。クラスを封印すると、生活がずっと楽になることに注意してください。Dispose
新しい仮想メソッドなどを呼び出すためのオーバーライドのパターンはDispose(bool)
、クラスが継承用に設計されている場合にのみ関連します。
これは少しとりとめのないものでしたが、どこに欲しいのか明確にしてください:)