我应该Dispose()DataSet和DataTable?
DataSet和DataTable都实现了IDisposable,所以,通过传统的最佳实践,我应该调用他们的Dispose()方法。
然而,从我目前阅读的内容来看,DataSet和DataTable实际上并没有任何非托pipe资源,所以Dispose()实际上并没有太多的function。
另外,我不能只使用using(DataSet myDataSet...)
因为DataSet有一个DataTables的集合。
所以,为了安全起见,我需要遍历myDataSet.Tables,处理每个DataTable,然后处理DataSet。
那么,在我所有的DataSets和DataTables上调用Dispose()是否值得麻烦?
附录:
对于那些认为DataSet应该被处置的人来说:一般来说,处置模式是使用using
或try..finally
,因为你要保证Dispose()将被调用。
然而,这对于一个集合来说真的很难实现。 例如,如果对Dispose()的调用之一抛出exception,你会怎么做? 你是否吞下它(这是“坏”),以便继续处理下一个元素?
或者,你是否build议我只是调用myDataSet.Dispose(),而忘记在myDataSet.Tables中configurationDataTables?
这里有几个讨论解释了为什么Dispose不需要DataSet。
处置还是不处理? :
DataSet中的Dispose方法只是因为inheritance的副作用而存在 – 换句话说,它在实际上并没有做任何有用的事情。
应该Dispose调用DataTable和DataSet对象? 包括MVP的一些解释:
system.data命名空间(ADONET)不包含非托pipe资源。 因此,只要你没有为自己添加一些特别的东西,就没有必要去处理这些东西。
了解Dispose方法和数据集? 有权威评论Scott Allen:
在实践中,我们很lessconfiguration数据集,因为它提供的好处很less“
所以,目前的共识是, 在DataSet上调用Dispose目前没有什么好的理由。
更新(2009年12月1日):
我想修改这个答案,并承认原来的答案是有缺陷的。
最初的分析确实适用于需要最终确定的对象,而且如果没有准确的深入理解,实践不应该被接受。
然而,事实certificate,DataSets,DataViews,DataTables 抑制了在构造函数中的终结 – 这就是为什么对它们调用Dispose()显式不做任何事情的原因。
据推测,这是因为他们没有非托pipe资源; 所以尽pipeMarshalByValueComponent允许非托pipe资源,这些特定的实现不需要,因此可以放弃最终确定。
(.NET作者会小心地压制通常占据大部分内存的types的最终定义,这说明了这种做法对于可终结types的重要性。)
尽pipe如此,自从.NET Framework(大约8年前)成立以来,这些细节仍然没有被logging下来,这是相当令人惊讶的(你基本上只能把自己的设备放在一起来筛选相互矛盾或模棱两可的材料,有时令人沮丧,但确实提供了对我们每天所依赖的框架的更全面的理解)。
大量阅读后,我的理解是:
如果一个对象需要终结,它可能会占用更多的内存 – 这是为什么:a)任何定义析构函数的types(或从定义析构函数的typesinheritance)被认为是可以终结的; b)分配时(在构造函数运行之前),一个指针放置在Finalization队列中; c)可终结对象通常需要回收2个集合 (而不是标准1); d)抑制结束不会从最终队列中删除一个对象(如SOS中的FinalizeQueue所报告的)这个命令是令人误解的; 知道最终化队列中的对象(本身)是没有帮助的; 知道什么对象是在最终确定队列,仍然需要最终确定将是有益的(是否有一个这样的命令?)
在对象的头文件中,抑制结束会稍微偏移一些,以向运行时指示它不需要调用Finalizer(不需要移动FReachable队列)。 它仍然在Finalization队列中(并继续由SOS中的FinalizeQueue报告)
DataTable,DataSet,DataView类全部植根于MarshalByValueComponent,一个可以(可能)处理非托pipe资源的可终结对象
- 因为DataTable,DataSet,DataView不引入非托pipe资源,所以它们在构造函数中禁止终结
- 虽然这是一种不寻常的模式,但它使得调用者不必担心在使用后调用Dispose
- 这和DataTables可能在不同数据集中共享的事实很可能是为什么DataSets不关心处理子DataTable
- 这也意味着这些对象将出现在SOS中的!FinalizeQueue下
- 但是,这些对象在收集之后仍然应该是可以回收的,就像它们的不可更新的对象一样
4(新的参考):
- http://www.devnewsgroups.net/dotnetframework/t19821-finalize-queue-windbg-sos.aspx
- http://blogs.msdn.com/tom/archive/2008/04/28/asp-net-tips-looking-at-the-finalization-queue.aspx
- http://issuu.com/arifaat/docs/asp_net_3.5unleashed
- http://msdn.microsoft.com/en-us/magazine/bb985013.aspx
- http://blogs.msdn.com/tess/archive/2006/03/27/561715.aspx
原始答案:
这里有很多误导性的,一般很差的答案 – 所有在这里降落的人都应该忽略噪音,并仔细阅读下面的参考文献。
毫无疑问,Dispose 应该在任何Finalizable对象上被调用。
数据表是可定制的。
调用Dispose 显着加快了内存的回收。
MarshalByValueComponent在其Dispose()中调用GC.SuppressFinalize(this) – 跳过这意味着在回收内存之前,不得不等待数十甚至数百个Gen0集合:
有了这个对定稿的基本理解,我们可以推断出一些非常重要的东西:
首先,需要完成的对象比没有对象的对象活得更长。 事实上,他们可以活得更久。 例如,假设gen2中的对象需要定型。 完成将被安排,但对象仍然在gen2,所以它不会被重新收集,直到下一代gen2收集发生。 这可能是一个很长的时间,事实上,如果事情进展顺利,将会是很长一段时间,因为gen2集合是昂贵的,因此我们希望它们很less发生。 需要完成的旧对象在回收空间之前可能需要等待几十个甚至几百个gen0集合。
其次,需要敲定的物品会造成附带损害。 由于内部对象指针必须保持有效,不仅直接需要结束的对象会留在内存中,而且对象直接或间接引用的所有对象也将保留在内存中。 如果一个巨大的物体树被一个需要敲定的单个物体锚定,那么整棵树就会像我们刚刚讨论过的那样长时间地逗留下去。 因此,谨慎使用终结器并将它们放置在尽可能less有内部对象指针的对象上非常重要。 在我刚刚提供的树形结构示例中,可以通过将需要定型的资源移动到单独的对象并在树的根中保留对该对象的引用来轻松避免此问题。 随着这种适度的变化,只有一个对象(希望是一个漂亮的小物体)将stream连忘返,最终成本降到最低。
最后,需要终结的对象为终结器线程创build工作。 如果你的最终化过程是一个复杂的过程,那么唯一的终结器线程将花费大量的时间来执行这些步骤,这可能会导致工作积压,从而导致更多的对象等待终结。 因此,终结者做尽可能less的工作是非常重要的。 还要记住,虽然所有对象指针在最终确定期间保持有效,但是这些指针可能会导致已经完成的对象,因此可能不太有用。 即使指针是有效的,避免在最终化代码中遵循对象指针也是最安全的。 一个安全,简短的终结代码path是最好的。
从在Gen2中看到100个未引用的DataTable的MB的人那里得到它:这非常重要,完全被这个线程的答案所遗漏。
参考文献:
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使用集电极-性能- finalizedispose-pattern.aspx
3 – http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/
你应该假定它做了一些有用的事情,即使它在当前没有做任何事情,也可以调用Dispose。 NET Framework的化身,但不能保证它将在未来的版本中保持这种方式导致资源使用效率低下。
即使对象没有非托pipe资源,处置可能会通过中断对象图来帮助GC。 一般来说,如果对象实现了IDisposable,那么应该调用Dispose()。
Dispose()是否实际执行某些操作取决于给定的类。 在DataSet的情况下,Dispose()实现从MarshalByValueComponentinheritance。 它从容器中移除并调用Disposed事件。 源代码如下(反汇编.NETreflection器):
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); } } } } }
你自己创buildDataTable? 因为通过任何对象的子元素(如在DataSet.Tables中)迭代通常是不需要的,因为它是Parent的工作来处理它的所有子成员。
一般来说,规则是:如果您创build了它,并且它实现了IDisposable,请处理它。 如果你没有创build它,那么不要处理它,这是父对象的工作。 但是每个对象都可能有特殊的规则,请查看文档。
对于.net 3.5,它明确地写着“不再使用时处置它”,所以这就是我要做的。
我随时调用一个对象实现IDisposeable的处置。 这是有原因的。
数据集可能是巨大的内存。 他们越早被标记清理,越好。
更新
我回答了这个问题已经5年了。 我仍然同意我的回答。 如果有处理方法,则应在对象完成时调用它。 IDispose接口的实现是有原因的。
如果您的意图或此问题的上下文真的是垃圾回收,那么您可以将数据集和数据表显式设置为null或使用关键字using并让它们超出范围。 像Tetraneutron早些时候说的那样,处置并没有太大的作用。 GC将收集不再被引用的数据集对象以及那些超出范围的对象。
我真的希望这样做迫使人们投票之前,实际上写下评论,然后再回答问题。
数据集实现IDisposable彻底MarshalByValueComponent,实现IDisposable。 由于数据集被pipe理,调用dispose没有真正的好处。
首先,我将检查Dispose对DataSet的作用。 也许使用redgate的reflection器将会有所帮助。
尝试使用Clear()函数。 这对我的处置很好。
DataTable dt = GetDataSchema(); //populate dt, do whatever... dt.Clear();