web-dev-qa-db-ja.com

DataSetとDataTableをDispose()する必要がありますか?

DataSetとDataTableはどちらもIDisposableを実装しているため、従来のベストプラクティスでは、Dispose()メソッドを呼び出す必要があります。

ただし、これまでに読んだことから、DataSetとDataTableには実際には管理されていないリソースがないため、Dispose()は実際には多くのことを行いません。

さらに、DataSetにはDataTableのコレクションがあるため、using(DataSet myDataSet...)だけを使用することはできません。

したがって、安全にするために、myDataSet.Tablesを反復処理し、各DataTablesを破棄してから、DataSetを破棄する必要があります。

だから、私のDataSetとDataTableのすべてでDispose()を呼び出すのは面倒な価値がありますか?

補遺:

DataSetを破棄する必要があると考えている場合:一般に、破棄のパターンはusingまたはtry..finallyを使用することです。これは、Dispose()が呼び出されることを保証するためです。

ただし、コレクションの場合、これは非常に速くfastくなります。たとえば、Dispose()の呼び出しの1つが例外をスローした場合はどうしますか?次の要素を破棄し続けることができるように、それを飲み込みますか(「悪い」)。

または、myDataSet.Dispose()を呼び出すだけで、myDataSet.TablesのDataTablesを破棄することを忘れることを提案しますか?

187
mbeckish

ここでは、DisposeがDataSetに必要ない理由を説明するいくつかの議論があります。

破棄するか、破棄しないか

DataSetのDisposeメソッドは、継承の副作用のためにのみ存在します。つまり、実際にはファイナライズに役立つものは何もしません。

DataTableおよびDataSetオブジェクトでDisposeを呼び出す必要がありますか? MVPからの説明が含まれています:

System.data名前空間(ADONET)には、アンマネージリソースは含まれません。したがって、何か特別なものを追加していない限り、それらを処分する必要はありません。

Disposeメソッドとデータセットを理解していますか? には、スコットアレン当局からのコメントがあります。

実際には、DataSetはほとんど利点がないため、DataSetを破棄することはほとんどありません」

そのため、現在のところ、DataSetでDisposeを呼び出す正当な理由はないというコンセンサスがあります。

139
DOK

更新(2009年12月1日):

この回答を修正し、元の回答に欠陥があったことを認めたいと思います。

元の分析doesは、ファイナライズを必要とするオブジェクトに適用されます。正確で詳細な理解がなくても表面上で受け入れられています。

ただし、DataSets、DataViews、DataTablesは、コンストラクターでファイナライズを抑制します。これが、明示的にDispose()を呼び出しても何もしない理由です。

おそらく、管理されていないリソースがないためです。 MarshalByValueComponentが管理されていないリソースを許可するという事実にもかかわらず、これらの特定の実装には必要性がないため、ファイナライズを控えることができます。

(その.NET作成者は、通常、最も多くのメモリを占有する型のファイナライズを抑制するように注意しますが、ファイナライズ可能な型に対するこのプラクティスの一般的な重要性を示しています。)

それにも関わらず、.NET Frameworkの開始(ほぼ8年前)が非常に驚くべきことなので、これらの詳細はまだ文書化されていません(本質的に、競合する曖昧な素材を選別するために自分のデバイスに任せていることになります)時にはイライラすることもありますが、私たちが日々依存しているフレームワークをより完全に理解できるようになります)。

たくさん読んだ後、ここに私の理解があります:

オブジェクトがファイナライズを必要とする場合、couldは必要以上にメモリを占有します–理由は次のとおりです。 )デストラクタを定義する(またはデストラクタを定義する型から継承する)型はすべてファイナライズ可能と見なされます。 b)割り当て時に(コンストラクターが実行される前に)、ポインターがファイナライズキューに置かれます。 c)ファイナライズ可能なオブジェクトでは、通常、2コレクションを回収する必要があります(標準1ではなく)。 d)ファイナライズを抑制しても、オブジェクトはファイナライズキューから削除されません(SOSの!FinalizeQueueによって報告されます)。このコマンドは誤解を招きます。ファイナライズキューにあるオブジェクト(およびそれ自体)を知ることは役に立ちません。ファイナライズキューにどのオブジェクトがあり、それでもファイナライズが必要かを知ることは役立ちます(このためのコマンドはありますか?)

ファイナライズを抑制すると、オブジェクトのヘッダーでビットがオフになり、ランタイムに対して、ファイナライザーを呼び出す必要がないことを示します(FReachableキューを移動する必要はありません)。ファイナライズキューに残ります(SOSの!FinalizeQueueによって引き続き報告されます)

DataTable、DataSet、DataViewクラスはすべて、MarshalByValueComponentに基づいています。MarshalByValueComponentは、アンマネージリソースを(潜在的に)処理できるファイナライズ可能なオブジェクトです。

  • DataTable、DataSet、DataViewはアンマネージリソースを導入しないため、コンストラクターでのファイナライズを抑制します
  • これは異常なパターンですが、使用後にDisposeを呼び出すことを心配する必要がありません。
  • これと、DataTablesが異なるDataSetsで共有される可能性があるという事実が、DataSetsが子DataTablesの破棄を気にしない理由です。
  • これは、これらのオブジェクトがSOSの!FinalizeQueueの下に表示されることも意味します
  • ただし、これらのオブジェクトは、ファイナライズされていない対応物のように、単一のコレクションの後でも回収可能でなければなりません

4(新しい参照):

元の答え:

これには多くの誤解を招くような一般的に非常に貧弱な答えがあります-ここに着陸した人は誰でもノイズを無視し、以下の参考文献を注意深く読んでください。

間違いなく、FinalizableオブジェクトでDisposeを呼び出す必要があります

DataTablesareファイナライズ可能。

Disposeを大幅に呼び出すと、メモリの再利用が高速化されます。

MarshalByValueComponent呼び出しGC.SuppressFinalize(this)そのDispose()で-これをスキップすると、メモリが回収されるまでに何百ものGen0コレクションがなければ、数十のコレクションを待つ必要があります。

ファイナライズのこの基本的な理解により、すでにいくつかの非常に重要なことを推測できます。

まず、ファイナライズを必要とするオブジェクトは、必要としないオブジェクトよりも長く存続します。実際、彼らはもっと長く生きることができます。たとえば、gen2にあるオブジェクトをファイナライズする必要があるとします。ファイナライズはスケジュールされますが、オブジェクトはまだgen2にあるため、次のgen2コレクションが発生するまでオブジェクトは再収集されません。実際、それは非常に長い時間になる可能性があります。実際、物事が順調に進んでいる場合、gen2コレクションはコストがかかるため、非常にまれにしか発生しないため、時間がかかります。ファイナライズを必要とする古いオブジェクトは、何百ものgen0コレクションがスペースを取り戻すまでに数十を待たなければならない場合があります。

第二に、ファイナライズを必要とするオブジェクトは、付随的な損傷を引き起こします。内部オブジェクトポインターは有効なままでなければならないため、ファイナライズを直接必要とするオブジェクトがメモリに残るだけでなく、オブジェクトが直接および間接的に参照するすべてのものもメモリに残ります。オブジェクトの巨大なツリーが、ファイナライズを必要とする単一のオブジェクトによって固定されている場合、ツリー全体が残ります。したがって、ファイナライザを控えめに使用し、内部オブジェクトポインタができるだけ少ないオブジェクトに配置することが重要です。先ほど説明したツリーの例では、ファイナライズが必要なリソースを別のオブジェクトに移動し、ツリーのルートでそのオブジェクトへの参照を保持することで、問題を簡単に回避できます。そのわずかな変更では、1つのオブジェクト(できればNiceの小さなオブジェクト)のみが残り、ファイナライズコストは最小限に抑えられます。

最後に、ファイナライズを必要とするオブジェクトは、ファイナライザースレッドの作業を作成します。ファイナライズプロセスが複雑な場合、唯一のファイナライザースレッドがこれらの手順を実行するのに多くの時間を費やします。これにより、作業のバックログが発生し、より多くのオブジェクトがファイナライズを待機し続ける可能性があります。したがって、ファイナライザができる限り少ない作業を行うことが非常に重要です。また、すべてのオブジェクトポインターはファイナライズ中に有効のままですが、それらのポインターが既にファイナライズされているオブジェクトにつながる場合があり、そのため役に立たない可能性があります。一般に、ポインタが有効であっても、ファイナライズコードでオブジェクトポインタをたどらないようにするのが最も安全です。安全で短いファイナライズコードパスが最適です。

Gen2で数百MBの参照されていないDataTablesを見た人からそれを取ります:これは非常に重要であり、このスレッドの回答では完全に見逃されています。

参照:

1 - http://msdn.Microsoft.com/en-us/library/ms973837.aspx

2 - http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entryhttp://www.dotnetfunda.com /articles/article524-net-best-practice-no-2-improve-garbage-collector-performance-using-finalizedispose-pattern.aspx

- http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/

123
Nariman

あなたはそれが有用な何かをすると仮定し、たとえそれがcurrentで何もしなくてもDisposeを呼び出すべきです。 NET Frameworkの化身、リソースの非効率的な使用につながる将来のバージョンでもそのように維持される保証はありません。

22
Nuno

オブジェクトにアンマネージリソースがない場合でも、破棄することでオブジェクトグラフを壊してGCに役立つ場合があります。一般に、オブジェクトがIDisposableを実装する場合、Dispose()を呼び出す必要があります。

Dispose()が実際に何かを行うかどうかは、指定されたクラスによって異なります。 DataSetの場合、Dispose()実装はMarshalByValueComponentから継承されます。コンテナから自身を削除し、Disposedイベントを呼び出します。ソースコードは以下のとおりです(.NET Reflectorで逆アセンブル)。

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this)
        {
            if ((this.site != null) && (this.site.Container != null))
            {
                this.site.Container.Remove(this);
            }
            if (this.events != null)
            {
                EventHandler handler = (EventHandler) this.events[EventDisposed];
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            }
        }
    }
}
16
dwieczor

DataTablesを自分で作成しますか? Objectの子(DataSet.Tablesのように)を反復処理することは、通常は必要ありません。なぜなら、すべての子メンバーを破棄するのは親の仕事だからです。

通常、ルールは次のとおりです。作成してIDisposableを実装している場合は、破棄します。作成しなかった場合は、破棄しないでください。それが親オブジェクトの仕事です。ただし、各オブジェクトには特別なルールがあります。ドキュメントを確認してください。

.net 3.5の場合、「もう使用しないときは破棄する」と明示的に指定されているため、それを実行します。

7
Michael Stum

オブジェクトがIDisposeableを実装するときはいつでもdisposeを呼び出します。それには理由があります。

DataSetは巨大なメモリを大量に消費する可能性があります。より早くクリーンアップのマークを付けることができれば、より良い結果が得られます。

更新

この質問に答えてから5年が経ちました。私はまだ答えに同意します。 disposeメソッドがある場合は、オブジェクトの処理が完了したときに呼び出す必要があります。 IDisposeインターフェイスは、理由のために実装されました。

6
Chuck Conway

この質問の意図またはコンテキストが本当にガベージコレクションである場合、データセットとデータテーブルを明示的にnullに設定するか、キーワードus​​ingを使用して、それらをスコープ外に移動できます。 Tetraneutronが以前に言ったように、Disposeはあまり効果がありません。 GCは、参照されなくなったデータセットオブジェクトと、スコープ外のデータセットオブジェクトを収集します。

SOが投票を強制的に下げて、答えを下げる前に実際にコメントを書くことを願っています。

4
Srikar Doddi

データセットは、IDisposableを実装するMarshalByValueComponentを通じてIDisposableを実装します。データセットは管理されているため、disposeを呼び出しても実質的なメリットはありません。

1
Tetraneutron

DataSetはMarshalByValueComponentクラスを継承し、MarshalByValueComponentはIDisposableインターフェイスを実装するため、Dispose()は不要です。

0

Clear()関数を使用してみてください。それは私にとっては処分に最適です。

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();
0
Hasan Savran