68

私が正しく理解していれば、.netランタイムは常に私の後にクリーンアップされます。したがって、新しいオブジェクトを作成し、コードでそれらを参照するのをやめると、ランタイムはそれらのオブジェクトをクリーンアップし、それらが占有していたメモリを解放します。

これが事実なので、なぜいくつかのオブジェクトはデストラクタまたはdisposeメソッドを持っている必要がありますか?それらが参照されなくなったときに、ランタイムはそれらの後でクリーンアップしませんか?

4

12 に答える 12

94

ファイナライザーは、ファイルハンドル、ソケット、カーネルオブジェクトなど、不足しているリソースがシステムに解放されることを保証するために必要です。ファイナライザーは常にオブジェクトの寿命の終わりに実行されるため、これらのハンドルを解放するための指定された場所です。

この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++デストラクタと人々を混同する傾向があるためです。

于 2008-12-01T18:49:52.970 に答える
21

前の答えは良いですが、ここでもう一度重要な点を強調させてください。特に、あなたはそれを言いました

私が正しく理解していれば、.netランタイムは常に私の後にクリーンアップされます。

これは部分的に正しいだけです。実際、.NETは、メインメモリという1つの特定のリソースの自動管理のみを提供します。他のすべてのリソースは手動でクリーンアップする必要があります。1)

奇妙なことに、メインメモリはプログラムリソースに関するほとんどすべての議論で特別なステータスを取得します。もちろん、これには十分な理由があります。メインメモリは、多くの場合、最も希少なリソースです。ただし、他の種類のリソースもあり、それらも管理する必要があることを覚えておく価値があります。


1)通常試みられる解決策は、他のリソースの存続期間をコード内のメモリ位置または識別子の存続期間に結合することです。したがって、ファイナライザーが存在します。

于 2008-12-01T19:08:00.267 に答える
9

ガベージコレクタは、システムが実際にメモリを解放する必要がない限り、システムにメモリが不足していない場合にのみ実行されます。つまり、GCがいつ実行されるかを確認することはできません。

ここで、あなたがデータベース接続であると想像してください。後でGCをクリーンアップすると、データベースに必要以上に長く接続され、奇妙なロード状況が発生する可能性があります。その場合、IDisposableを実装して、ユーザーがDispose()を呼び出すか、using()を使用して、後で実行される可能性のあるGCに依存することなく、接続ができるだけ早く閉じられるようにします。

通常、IDisposableは、管理されていないリソースで動作するすべてのクラスに実装されます。

于 2008-12-01T18:51:04.577 に答える
4
  1. ガベージコレクターがあなたの後にクリーンアップできないことがあります
  2. クリーンアップできるものでも、より早くクリーンアップするのに役立ちます
于 2008-12-01T18:55:17.920 に答える
2

本当の理由は、.netガベージコレクションが管理されていないリソースを収集するように設計されていないためですしたがって、これらのリソースのクリーンアップは依然として開発者の手に委ねられています。また、オブジェクトがスコープ外になったときに、オブジェクトファイナライザーが自動的に呼び出されることはありません。それらは、未定の時間にGCによって呼び出されます。そして、それらが呼び出されると、GCはすぐにそれを実行せず、次のラウンドがそれを呼び出すのを待ち、クリーンアップする時間をさらに増やします。オブジェクトが管理されていないリソース(ファイルなど)をほとんど保持していない場合は、これは良いことではありません。またはネットワーク接続)。使い捨てパターンを入力します。開発者は、決められた時間に(yourobject.Dispose()またはusing(...)ステートメントを呼び出すときに)不足しているリソースを手動で解放できます。GC.SuppressFinalize(this);を呼び出す必要があることに注意してください。disposeメソッドで、オブジェクトが手動で破棄され、ファイナライズされるべきではないことをGCに通知します。K.CwalinaとB.AbramsによるFrameworkDesignGuidelinesの本をご覧になることをお勧めします。Disposableパターンが非常によく説明されています。

幸運を!

于 2008-12-01T19:01:15.490 に答える
2

簡単な説明:

  • Disposeは、メモリ以外のリソース、特に不足しているリソースを確定的に破棄するように設計されています。たとえば、ウィンドウハンドルやデータベース接続などです。
  • Finalizeは、非メモリリソースを非決定的に破棄するように設計されており、通常、Disposeが呼び出されなかった場合のバックストップとして使用されます。

Finalizeメソッドを実装するためのいくつかのガイドライン:

  • Finalizeメソッドにはパフォーマンスコストが伴うため、Finalizeを必要とするオブジェクトにのみFinalizeを実装してください。
  • Finalizeメソッドが必要な場合は、IDisposableを実装して、タイプのユーザーがFinalizeメソッドを呼び出すコストを回避できるようにすることを検討してください。
  • Finalizeメソッドは、パブリックではなく保護する必要があります。
  • Finalizeメソッドは、タイプが所有するすべての外部リソースを解放する必要がありますが、それが所有する外部リソースのみを解放する必要があります。他のリソースを参照するべきではありません。
  • CLRは、Finalizeメソッドが呼び出される順序については保証しません。Danielがコメントで述べているように、これは、Finalizeメソッドがメンバー参照型にアクセスできないことを意味します。これらは独自のファイナライザーを持っている(またはいつか持っている)可能性があるためです。
  • タイプの基本タイプ以外のタイプでFinalizeメソッドを直接呼び出さないでください。
  • Finalizeメソッドで未処理の例外を回避するようにしてください。これにより、プロセスが終了します(2.0以降)。
  • Finalizerメソッドで長時間実行されるタスクを実行しないでください。これにより、Finalizerスレッドがブロックされ、他のFinalizerメソッドが実行されなくなります。

Disposeメソッドを実装するためのいくつかのガイドライン:

  • 明示的に解放する必要のあるリソースをカプセル化するタイプに、disposeデザインパターンを実装します。
  • 基本タイプが保持していない場合でも、リソースを保持する1つ以上の派生型を持つ基本タイプにdisposeデザインパターンを実装します。
  • インスタンスでDisposeが呼び出された後、GC.SuppressFinalizeメソッドを呼び出して、Finalizeメソッドが実行されないようにします。このルールの唯一の例外は、DisposeでカバーされていないFinalizeで作業を行わなければならないというまれな状況です。
  • Disposeが呼び出されると想定しないでください。タイプが所有するアンマネージリソースも、Disposeが呼び出されない場合に備えて、Finalizeメソッドで解放する必要があります。
  • リソースがすでに破棄されている場合は、このタイプ(Dispose以外)のインスタンスメソッドからObjectDisposedExceptionをスローします。このルールは、例外をスローせずに複数回呼び出すことができる必要があるため、Disposeメソッドには適用されません。
  • 基本タイプの階層を介してDisposeの呼び出しを伝播します。Disposeメソッドは、このオブジェクトとこのオブジェクトが所有するすべてのオブジェクトが保持するすべてのリソースを解放する必要があります。
  • Disposeメソッドが呼び出された後は、オブジェクトを使用できないようにすることを検討する必要があります。すでに廃棄されているオブジェクトを再作成することは、実装が難しいパターンです。
  • 例外をスローせずに、Disposeメソッドを複数回呼び出すことができるようにします。メソッドは、最初の呼び出しの後は何もしません。
于 2008-12-01T19:24:58.157 に答える
1

デスクリュクタとdisposeメソッドを必要とするオブジェクトは、管理されていないリソースを使用しています。したがって、ガベージコレクタはこれらのリソースをクリーンアップできず、自分でこれを実行する必要があります。

IDisposableについてはMSDNのドキュメントを参照してください。http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

この例では、管理されていないハンドラーであるIntPrを使用しています。

于 2008-12-01T18:50:31.473 に答える
0

一部のオブジェクトは、低レベルのアイテムをクリーンアップする必要がある場合があります。閉じる必要のあるハードウェアなど。

于 2008-12-01T18:49:44.703 に答える
0

主に非マネージコード、および非マネージコードとの相互作用のため。「純粋な」マネージコードには、ファイナライザーは必要ありません。一方、使い捨ては、使い終わったときに何かを強制的に解放するための便利なパターンです。

于 2008-12-01T18:50:38.837 に答える
0

.NETガベージコレクターは、.NETランタイム内で管理対象オブジェクトを処理する方法を知っています。ただし、Disposeパターン(IDisposable)は、主にアプリケーションが使用している管理対象外のオブジェクトに使用されます。

言い換えると、.NETランタイムは、すべての種類のデバイスを処理する方法や、そこで処理する方法(ネットワーク接続、ファイルハンドル、グラフィックスデバイスなどを閉じる)を必ずしも知っているわけではないため、IDisposableを使用すると、「タイプに「独自のクリーンアップを実装する」。その実装を確認すると、ガベージコレクターはDispose()を呼び出して、管理対象ヒープの外部にあるものが確実にクリーンアップされるようにすることができます。

于 2008-12-01T18:51:36.057 に答える
0

純粋な管理対象オブジェクトが使用されなくなったときに特定のアクションを実行する必要がある場合がいくつかあります(ごくわずかです)。頭のてっぺんから例を思い付くことができませんが、いくつか見ました。長年にわたる合法的な使用の。ただし、主な理由は、オブジェクトが使用している可能性のある管理されていないリソースをクリーンアップすることです。

したがって、一般に、管理されていないリソースを使用している場合を除き、Dispose/Finalizeパターンを使用する必要はありません。

于 2008-12-01T18:53:32.993 に答える
0

ガベージコレクターは、管理対象環境が割り当てなかったものを収集できないためです。したがって、メモリ割り当てをもたらすアンマネージAPIの呼び出しは、従来の方法で収集する必要があります。

于 2008-12-01T18:56:06.407 に答える