更新(2009年12月1日):
この回答を修正し、元の回答に欠陥があることを認めたいと思います。
元の分析は、ファイナライズが必要なオブジェクトに適用されます。正確で詳細な理解がなければ、実践を表面的に受け入れるべきではないという点は依然として存在します。
ただし、DataSets、DataViews、DataTablesは、コンストラクターでのファイナライズを抑制していることがわかりました。これが、それらに対してDispose()を明示的に呼び出しても何も起こらない理由です。
おそらく、これは管理されていないリソースがないために発生します。したがって、 MarshalByValueComponentが管理されていないリソースを考慮に入れているにもかかわらず、これらの特定の実装には必要がないため、ファイナライズを省略できます。
(.NETの作成者は、通常最も多くのメモリを占有するタイプのファイナライズを抑制するように注意します。これは、ファイナライズ可能なタイプの一般的なこの方法の重要性を示しています。)
それにもかかわらず、.NET Frameworkの開始(ほぼ8年前)以来、これらの詳細はまだ十分に文書化されていません(本質的に、競合するあいまいな資料をふるいにかけて断片をまとめるのは自分のデバイスに任されています)。時々イライラしますが、私たちが日常的に依存しているフレームワークのより完全な理解を提供します)。
たくさん読んだ後、ここに私の理解があります:
オブジェクトがファイナライズを必要とする場合、必要以上にメモリを占有する可能性があります。その理由は次のとおりです。a)デストラクタを定義する(またはデストラクタを定義するタイプから継承する)タイプは、ファイナライズ可能と見なされます。b)割り当て時に(コンストラクターが実行される前に)、ポインターがファイナライズキューに配置されます。c)ファイナライズ可能なオブジェクトでは、通常、(標準の1つではなく)2つのコレクションを再利用する必要があります。d)ファイナライズを抑制しても、ファイナライズキューからオブジェクトは削除されません(SOSの!FinalizeQueueによって報告されます)このコマンドは誤解を招く可能性があります。(それ自体で)ファイナライズキューにあるオブジェクトを知ることは役に立ちません。どのオブジェクトがファイナライズキューにあり、それでもファイナライズが必要かを知ることは役に立ちます(これに対するコマンドはありますか?)
ファイナライズを抑制すると、オブジェクトのヘッダーが少しオフになり、ファイナライザーを呼び出す必要がないことをランタイムに示します(FReachableキューを移動する必要はありません)。それはファイナライズキューに残ります(そしてSOSの!FinalizeQueueによって報告され続けます)
DataTable、DataSet、DataViewクラスはすべて、管理されていないリソースを(潜在的に)処理できるファイナライズ可能なオブジェクトであるMarshalByValueComponentをルートとしています。
- DataTable、DataSet、DataViewはアンマネージリソースを導入しないため、コンストラクターでのファイナライズを抑制します
- これは珍しいパターンですが、発信者は使用後にDisposeを呼び出すことを心配する必要がありません。
- これと、DataTablesが異なるDataSet間で共有される可能性があるという事実は、DataSetsが子DataTablesを破棄することを気にしない理由である可能性があります
- これは、これらのオブジェクトがSOSの!FinalizeQueueの下に表示されることも意味します
- ただし、これらのオブジェクトは、ファイナライズできない対応物と同様に、単一のコレクションの後で再利用可能である必要があります。
4(新しい参照):
元の回答:
これには多くの誤解を招くような一般的に非常に貧弱な答えがあります-ここに着陸した人は誰でもノイズを無視し、以下の参考文献を注意深く読む必要があります。
間違いなく、DisposeはFinalizableオブジェクトで呼び出す必要があります。
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/