私が正しく理解していれば、.netランタイムは常に私の後にクリーンアップされます。したがって、新しいオブジェクトを作成し、コードでそれらを参照するのをやめると、ランタイムはそれらのオブジェクトをクリーンアップし、それらが占有していたメモリを解放します。
これが事実なので、なぜいくつかのオブジェクトはデストラクタまたはdisposeメソッドを持っている必要がありますか?それらが参照されなくなったときに、ランタイムはそれらの後でクリーンアップしませんか?
ファイナライザーは、ファイルハンドル、ソケット、カーネルオブジェクトなど、不足しているリソースがシステムに解放されることを保証するために必要です。ファイナライザーは常にオブジェクトの寿命の終わりに実行されるため、これらのハンドルを解放するための指定された場所です。
Dispose
パターンは、リソースの決定論的破壊を提供するために使用されます。 .netランタイムガベージコレクターは非決定論的であるため(つまり、ランタイムが古いオブジェクトを収集してファイナライザーを呼び出すタイミングがわからない)、システムリソースの決定論的リリースを保証するメソッドが必要でした。したがって、Dispose
パターンを適切に実装すると、リソースの決定論的な解放が提供され、コンシューマーが不注意でオブジェクトを破棄しない場合、ファイナライザーはオブジェクトをクリーンアップします。
Dispose
が必要な理由の簡単な例は、迅速でダーティなログメソッドです。
public void Log(string line)
{
var sw = new StreamWriter(File.Open(
"LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None));
sw.WriteLine(line);
// Since we don't close the stream the FileStream finalizer will do that for
// us but we don't know when that will be and until then the file is locked.
}
上記の例では、ガベージコレクターがStreamWriter
オブジェクトのファイナライザーを呼び出すまでファイルはロックされたままになります。その間、ログを書き込むためにメソッドが再度呼び出される可能性があるため、これには問題がありますが、ファイルがまだロックされているため、今回は失敗します。
正しい方法は、オブジェクトの使用が終了したら、オブジェクトを破棄することです。
public void Log(string line)
{
using (var sw = new StreamWriter(File.Open(
"LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))) {
sw.WriteLine(line);
}
// Since we use the using block (which conveniently calls Dispose() for us)
// the file well be closed at this point.
}
ところで、技術的にファイナライザーとデストラクタは同じ意味です。私はc#デストラクタを「ファイナライザ」と呼ぶことを好みます。そうしないと、C#とは異なり、決定論的であるC++デストラクタと人々を混同する傾向があるためです。
前の答えは良いですが、ここでもう一度重要な点を強調させてください。特に、あなたはそれを言いました
私が正しく理解していれば、.netランタイムは常に私の後にクリーンアップされます。
これは部分的に正しいだけです。実際、。NETonlyは、1つの特定のリソースの自動管理を提供します:メインメモリ。他のすべてのリソースは手動でクリーンアップする必要があります。1)
奇妙なことに、メインメモリはプログラムリソースに関するほとんどすべての議論で特別なステータスを取得します。もちろん、これには正当な理由があります。メインメモリが最も不足しているリソースであることがよくあります。ただし、他の種類のリソースもあり、それらも管理する必要があることを覚えておく価値があります。
1) 通常試みられる解決策は、他のリソースの存続期間を、コード内のメモリ位置または識別子の存続期間に結合することです。したがって、ファイナライザーが存在します。
ガベージコレクタは、システムが実際にメモリを解放する必要がない限り、システムにメモリ不足がない場合にのみ実行されます。つまり、GCがいつ実行されるかを確信することはできません。
ここで、あなたがデータベース接続であると想像してください。後でGCをクリーンアップすると、データベースに必要以上に長く接続され、奇妙なロード状況が発生する可能性があります。その場合、IDisposableを実装して、ユーザーがDispose()を呼び出すか、using()を使用して、後で実行される可能性のあるGCに依存することなく、接続ができるだけ早く閉じられるようにします。
通常、IDisposableは、管理されていないリソースで機能するすべてのクラスに実装されます。
本当の理由は、.netガベージコレクションが収集するように設計されていないためです管理されていないリソースしたがって、これらのリソースのクリーンアップは依然として開発者の手に委ねられています。また、オブジェクトがスコープ外になったときに、オブジェクトファイナライザーが自動的に呼び出されることはありません。それらは、不確定な時間にGCによって呼び出されます。そして、それらが呼び出されると、GCはすぐにそれを実行せず、次のラウンドがそれを呼び出すのを待ち、クリーンアップする時間をさらに増やします。オブジェクトが管理されていないリソース(ファイルなど)をほとんど保持していない場合は、良いことではありません。またはネットワーク接続)。使い捨てパターンを入力します。開発者は、決められた時間に(yourobject.Dispose()またはusing(...)ステートメントを呼び出すときに)不足しているリソースを手動で解放できます。 GC.SuppressFinalize(this);を呼び出す必要があることに注意してください。 disposeメソッドで、オブジェクトが手動で破棄され、ファイナライズされるべきではないことをGCに通知します。 K.CwalinaとB.AbramsによるFrameworkDesignGuidelinesの本をご覧になることをお勧めします。 Disposableパターンが非常によく説明されています。
幸運を!
簡単な説明:
Finalizeメソッドを実装するためのいくつかのガイドライン:
Disposeメソッドを実装するためのいくつかのガイドライン:
デスクリュクタとdisposeメソッドを必要とするオブジェクトは、管理されていないリソースを使用しています。したがって、ガベージコレクタはこれらのリソースをクリーンアップできず、自分でこれを行う必要があります。
IDisposableについてはMSDNのドキュメントを参照してください。 http://msdn.Microsoft.com/en-us/library/system.idisposable.aspx
この例では、管理されていないハンドラーであるIntPrを使用しています。
一部のオブジェクトは、低レベルのアイテムをクリーンアップする必要がある場合があります。閉じる必要のあるハードウェアなど。
主に非マネージコード、および非マネージコードとの相互作用のため。 「純粋な」マネージコードにはファイナライザーは必要ありません。一方、使い捨ては、使い終わったときに何かを強制的に解放するための便利なパターンです。
.NETガベージコレクターは、.NETランタイム内で管理対象オブジェクトを処理する方法を知っています。ただし、Disposeパターン(IDisposable)は、主にアプリケーションが使用している管理対象外のオブジェクトに使用されます。
言い換えると、.NETランタイムは、すべての種類のデバイスを処理する方法や、そこにある処理(ネットワーク接続、ファイルハンドル、グラフィックスデバイスなどを閉じる)を必ずしも知っているわけではないため、IDisposableを使用すると、「タイプに「自分のクリーンアップを実装する」。その実装を確認すると、ガベージコレクターはDispose()を呼び出して、マネージヒープ外のものが確実にクリーンアップされるようにすることができます。
純粋な管理対象オブジェクトが使用されなくなったときに特定のアクションを実行する必要がある場合がいくつかあります(ごくわずかです)。頭のてっぺんから例を思い付くことができませんが、いくつか見ました。長年にわたる合法的な使用の。ただし、主な理由は、オブジェクトが使用している可能性のある管理されていないリソースをクリーンアップすることです。
したがって、一般に、管理されていないリソースを使用している場合を除き、Dispose/Finalizeパターンを使用する必要はありません。
ガベージコレクタは、管理環境が割り当てなかったものを収集できないためです。したがって、メモリ割り当てが発生するアンマネージAPIの呼び出しは、従来の方法で収集する必要があります。