更新(2009年12月1日):
この回答を修正して、元の回答に欠陥があったことを認めたいと思います。
元の分析は、ファイナライズが必要なオブジェクトに適用されます。そして、正確で詳細な理解がなければ、表面上でプラクティスを受け入れるべきではないという点がまだあります。
ただし、DataSets、DataViews、DataTables はそれらのコンストラクターでのファイナライズを抑制していることがわかります。これが、それらに対してDispose()を呼び出しても明示的に何もしない理由です。
おそらく、管理されていないリソースがないためです。したがって、MarshalByValueComponentがアンマネージリソースを許可するという事実にもかかわらず、これらの特定の実装は必要がないため、ファイナライズを忘れることがあります。
(.NETの作成者は、通常、最も多くのメモリを占有する型そのもののファイナライズを抑制するよう注意を払うことになるので、ファイナライズ可能な型について、このプラクティスの一般的な重要性がわかります。)
それにもかかわらず、.NET Frameworkの開始(ほぼ8年前)はかなり驚くべきものであるため、これらの詳細はまだ十分に文書化されていません(本質的に、競合するあいまいな資料をふるいにかけるために独自のデバイスに任せて、断片をまとめる必要があります)時にはイライラしますが、私たちが日常的に使用しているフレームワークをより完全に理解できます)。
たくさん読んだ後、これが私の理解です:
オブジェクトがファイナライズを必要とする場合、必要以上に長くメモリを占有する可能性があります。理由は次のとおりです。a)デストラクタを定義する(またはデストラクタを定義する型から継承する)型はファイナライズ可能と見なされます。b)割り当て時に(コンストラクターが実行される前に)、ポインターがFinalizationキューに配置されます。c)ファイナライズ可能なオブジェクトは、通常、(標準の1ではなく)2つのコレクションを再生する必要があります。d)ファイナライズを抑制しても、オブジェクトはファイナライズキューから削除されません(SOSの!FinalizeQueueによって報告されます)。このコマンドは誤解を招く可能性があります。(それ自体の)ファイナライズキューにあるオブジェクトを知ることは役に立ちません。どのオブジェクトがファイナライズキューにあり、ファイナライズが必要かを知ることは役に立ちます(このためのコマンドはありますか?)
ファイナライズを抑制すると、オブジェクトのヘッダーでビットがオフになり、ファイナライザを呼び出す必要がないことをランタイムに示します(FReachableキューを移動する必要はありません)。ファイナライズキューに残ります(SOSの!FinalizeQueueによって引き続き報告されます)
DataTable、DataSet、DataViewクラスはすべて、管理されていないリソースを(場合によっては)処理できるファイナライズ可能なオブジェクトであるMarshalByValueComponentをルートとします。
- DataTable、DataSet、DataViewはアンマネージリソースを導入しないため、コンストラクターでのファイナライズを抑制します
- これは異常なパターンですが、使用後に発信者がDisposeの呼び出しについて心配する必要がなくなります。
- これと、DataTableが異なるDataSet間で共有される可能性があるという事実が、DataSetが子のDataTableを破棄する必要がない理由である可能性があります。
- これは、これらのオブジェクトがSOSの!FinalizeQueueの下に表示されることも意味します
- ただし、これらのオブジェクトは、ファイナライズ不可能なオブジェクトと同様に、単一のコレクションの後でも再利用可能である必要があります。
4(新しい参照):
元の回答:
これについては誤解を招きやすく、一般的には非常に貧弱な答えがたくさんあります。ここに上陸した人は、ノイズを無視して、以下のリファレンスを注意深く読んでください。
間違いなく、すべてのFinalizableオブジェクトでDisposeを呼び出す必要があります。
DataTables はファイナライズ可能です。
Disposeを呼び出すと、メモリの再利用が大幅に高速化されます。
MarshalByValueComponentは、Dispose()でGC.SuppressFinalize(this)を呼び出します -これをスキップすると、メモリが解放される前に、数百ではないにしても数十のGen0コレクションを待つ必要があります。
ファイナライズに関するこの基本的な理解があれば、すでにいくつかの非常に重要なことを推測できます。
まず、ファイナライズが必要なオブジェクトは、必要のないオブジェクトよりも長く存続します。実際、彼らは長生きすることができます。たとえば、gen2にあるオブジェクトをファイナライズする必要があるとします。ファイナライズがスケジュールされますが、オブジェクトはまだgen2にあるため、次のgen2コレクションが発生するまで再収集されません。これは実際には非常に長い時間になる可能性があり、実際、状況が順調であれば、gen2コレクションはコストがかかるため非常にまれにしか発生させないため、長い時間になるでしょう。ファイナライズが必要な古いオブジェクトは、数百とは言わないまでも数十のgen0コレクションを待ってから、スペースが解放される場合があります。
第二に、ファイナライズが必要なオブジェクトは、付随的な損傷を引き起こします。内部オブジェクトポインターは有効なままである必要があるため、ファイナライズを直接必要とするオブジェクトがメモリ内に残るだけでなく、オブジェクトが直接および間接的に参照するすべてのオブジェクトもメモリ内に残ります。オブジェクトの巨大なツリーが、ファイナライズを必要とする単一のオブジェクトによって固定されている場合、ツリー全体が残ります。したがって、ファイナライザは慎重に使用し、内部オブジェクトポインタができるだけ少ないオブジェクトに配置することが重要です。先ほど示したツリーの例では、ファイナライズが必要なリソースを別のオブジェクトに移動し、そのオブジェクトへの参照をツリーのルートに保持することで、問題を簡単に回避できます。
最後に、ファイナライズが必要なオブジェクトは、ファイナライザスレッドの作業を作成します。ファイナライズプロセスが複雑なプロセスである場合、唯一のファイナライザスレッドがこれらのステップの実行に多くの時間を費やします。これにより、作業のバックログが発生し、ファイナライズを待機するオブジェクトが増えます。したがって、ファイナライザができる限り少ない作業を行うことが非常に重要です。また、ファイナライズ中もすべてのオブジェクトポインターは有効なままですが、それらのポインターがすでにファイナライズされているオブジェクトにつながる可能性があるため、あまり役に立たない場合があることにも注意してください。ポインターが有効であっても、ファイナライゼーションコードでオブジェクトポインターを追跡しないのが一般的に最も安全です。安全で短いファイナライズコードパスが最適です。
Gen2で参照されていない数百MBのDataTableを目にした人からそれを取り上げてください。これは非常に重要であり、このスレッドの回答には完全に見落とされています。
参照:
1 -
http://msdn.microsoft.com/en-us/library/ms973837.aspx
2 -
http
: //vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage -collector-performance-using-finalizedispose-pattern.aspx
3 -
http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/