Dispose()DataSetおよびDataTableを実行する必要がありますか?


197

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

ただし、これまで読んだことから、DataSetとDataTableには実際にはアンマネージリソースがないため、Dispose()は実際にはあまり機能しません。

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

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

それで、私のすべてのDataSetsとDataTablesでDispose()を呼び出す手間は価値がありますか?

補遺:

DataSetを破棄する必要があると思う人のために:Dispose()が呼び出されることを保証する必要があるため、通常、破棄のパターンはusingor を使用するtry..finallyことです。

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

または、myDataSet.Dispose()を呼び出して、myDataSet.TablesのDataTableを破棄することを忘れることをお勧めしますか?


9
Disposeが例外をスローすることは想定されていません。もしそうなら、それはうまく書かれていないので、…{some.Dispose();を試してください。} catch {}で十分です。- blogs.msdn.com/b/clyon/archive/2004/09/23/233464.aspx
LukeSw

3
多くのDataSetオブジェクトを使用するアプリの1つで、明らかなメモリリークに気づきました。私は.Dispose()を呼び出したり、それらのオブジェクトに「using」ブロックを使用したりしていませんでした。それで、コードを調べ、DataSetまたはDataTableを作成していたすべての場所に「using」ブロックを追加しました。これでメモリが解放されました。.Dispose()が実際にはDataSetとDataTableに必要であることを私にしっかりと示しているようです。
dizzy.stackoverflow 2015

回答:


147

ここでは、DataSetにDisposeが必要ない理由を説明する2つのディスカッションを示します。

処分するかしないか?

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

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

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

Disposeメソッドとデータセットを理解していますか?当局スコットアレンからのコメント付き:

実際には、ほとんどメリットがないため、DataSetを破棄することはほとんどありません。 "

したがって、コンセンサスは、現在、DataSetでDisposeを呼び出す正当な理由がないということです。


7
提供されたリンクは、DataTableがFinalizableオブジェクトの一種であるという点を完全に外しています。下記のナリマンの回答をご覧ください。
ハーマン、

興味深い答えですが、SqlConnection、SqlCommand、SqlDataAdapterについては、Disposeを明示的に呼び出す必要がありますか?
ウィリー2014年

@ウィリー多くの人がIDisposablesのusingステートメントを使用していると思います。using(SqlConnection cn = new SqlConnection(connectionString)){using(SqlCommand cm = new SqlCommand(commandString、cn)){cn.Open(); cm.ExecuteNonQuery(); }}
DOK

1
@Willyはい、それらはアンマネージリソースを使用するため、絶対に破棄する必要があります。それを明示的に呼び出すか、usingブロックを使用して暗黙的に呼び出すかは、あなた次第です。
Dスタンリー

129

更新(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/


いい視点ね。多くのDataTableを持つDataSetがある場合、通常はどのようにコードを構造化しますか?ネストされたusingステートメントがたくさんありますか?一度にすべてを一度にクリーンアップしてみてください。
mbeckish

14
「しかし、DataSets、DataViews、DataTablesはコンストラクターでのファイナライズを抑制していることがわかります。これが、それらに対してDipose()を呼び出しても明示的に何もしない理由です。」2つの概念はほとんど無関係です。ファイナライズを抑制するものでも、Dispose()で何かを実行できます。確かに、それは実際に私たちはそれを逆にあれば、より理にかなって:廃棄()何も、行わない理由は何の関係も存在しないであろうから、それはコンストラクタでファイナライズを抑制し、すなわちそれは(finaliserを呼んでGCを気にする必要はありません。これは通常、disposeを呼び出します)。
マークグラベル

ありがとう。この議論はにも当てはまりますTableAdapterか?
Bondolin


24

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


将来的にIDisposableが実装される保証もありません。それがusing(...)のように単純であるならば、私はあなたに同意しますが、DataSetの場合、それは何の手間もかけないように思えます。
mbeckish 2009年

28
常にIDisposableを実装すると想定しても、かなり安全です。インターフェイスの追加または削除は重大な変更ですが、Disposeの実装の変更はそうではありません。
グレッグディーン

5
また、別のプロバイダーが、IDisposableで実際に何かを行う実装を持っている場合もあります。
Matt Spradley、2009年

言うまでもなく、これDataTableは封印されていません。実行しているときは大した問題ではありませんnew DataTableDataTable、引数として、またはメソッド呼び出しの結果としてを受け取る場合は非常に重要です。
Luaan 2016

17

オブジェクトにアンマネージリソースがない場合でも、破棄するとオブジェクトグラフが壊れて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);
                }
            }
        }
    }
}

1
確かに。ごく最近、いくつかのコードを見て、多くのDataTablesがDisposedにならずに非常に大きなループで作成されました。これにより、コンピュータですべてのメモリが消費され、メモリ不足になるとプロセスがクラッシュします。開発者にDataTableでdisposeを呼び出すように言った後、問題はなくなりました。
RichardOD 2009年

7

自分でDataTableを作成しますか?(DataSet.Tablesの場合のように)オブジェクトの子を反復処理する必要はありません。すべての子メンバーを破棄するのは親の仕事だからです。

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

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


4
私が理解していることから、一般的なコンセンサスは、オブジェクトは自身のアンマネージリソースを破棄する必要があるということです。しかし、IDisposableをオブジェクトのコレクションには、それぞれ1を配置し、その要素を通じて、一般的な反復ではないでしょう、コレクションのその要素の外に他の参照があるかもしれませんので: stackoverflow.com/questions/496722/...
mbeckish

1
確かに、コレクションは常に何かを「実行」するわけではないため、常に私が特別と考えるものです。それらは単に...コンテナーなので、私はそれについて気にしたことはありません。
Michael Stum

7

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

DataSetは巨大なメモリを消費する可能性があります。クリーンアップのマークを付けられるのが早ければ早いほどよい。

更新

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


5
disposeを呼び出しても、メモリの再利用が速くなることはありません。そのためには、ガベージコレクターを手動で起動する必要がありますが、これは一般に悪い計画です。
テトラニュートロン

2
Disposeが一連の参照をnullに設定すると、オブジェクトがコレクションの候補になり、それがなければスキップされる可能性があります。
グレッグディーン

1
Disposeの目的は、管理対象オブジェクトのメモリをクリアすることではありません。それがガベージコレクターの仕事です。ポイントは、管理されていないオブジェクトを消去することです。DataSetsにアンマネージ参照がないため、理論的には破棄して呼び出す必要がないという証拠があるようです。そうは言っても、私はDisposeを呼び出すために自分の邪魔をしなければならない状況にいたことは一度もありません。とにかくそれを呼び出すだけです。
cbp 2009年

4
IDisposable の主な用途は、アンマネージリソースを解放することです。多くの場合、破棄されたインスタンスにとって意味のある方法で状態を変更します。(プロパティをfalseに設定、参照をnullに設定など)
グレッグディーン

3
オブジェクトにdisposeメソッドがある場合、アンマネージオブジェクトをクリーンアップするためかどうかに関係なく、何らかの理由でそこに配置されました。
チャックコンウェイ

4

この質問の意図またはコンテキストが実際にガベージコレクションである場合は、データセットとデータテーブルを明示的にnullに設定するか、キーワードus​​ingを使用してスコープから外すことができます。処分は、テトラニュートロンが以前に言ったように多くを行いません。GCは、参照されなくなったデータセットオブジェクトとスコープ外のデータセットオブジェクトも収集します。

私は、回答に反対票を投じる前に、実際にコメントを書くように人々に反対投票を強いられたことを本当に望みます。


+ 1他の人に別の見方を検討させたくない人もいるでしょう。
DOK、

2
反対票を投じても、人々がさまざまな視点を検討することを決して拒否しません。
グレッグディーン

1

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


6
それは、後で何をするかを知っているだろう。
グレッグディーン

将来、すべてのコードが本来の機能を果たさなくなると推測するこの態度は、関係者全員の想定における苦痛です。
MicroservicesOnDDD

0

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

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

0

DataSetはMarshalByValueComponentクラスを継承し、MarshalByValueComponentはIDisposableインターフェイスを実装するため、Dispose()の必要はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.