我应该Dispose()DataSet和DataTable吗?

mbe*_*ish 187 datatable dispose idisposable using dataset

DataSet和DataTable都实现了IDisposable,因此,通过传统的最佳实践,我应该调用它们的Dispose()方法.

但是,从我到目前为止所读到的,DataSet和DataTable实际上并没有任何非托管资源,因此Dispose()实际上并没有做太多.

另外,我不能只使用,using(DataSet myDataSet...)因为DataSet有一组DataTables.

所以,为了安全起见,我需要遍历myDataSet.Tables,处理每个DataTable,然后处理DataSet.

那么,在我的所有DataSet和DataTables上调用Dispose()是否值得麻烦?

附录:

对于那些认为应该处理DataSet的人:通常,处理的模式是使用usingor try..finally,因为你想保证将调用Dispose().

然而,这对于一个集合来说真的很快.例如,如果对Dispose()的一个调用抛出异常,你会怎么做?你吞下它(这是"坏"),以便你可以继续处理下一个元素?

或者,你是否建议我只调用myDataSet.Dispose(),而忘记在myDataSet.Tables中处理DataTables?

DOK*_*DOK 140

以下是一些讨论,解释了为什么Dispose不是DataSet所必需的.

处置或不处理?:

DataSet中的Dispose方法仅存在因为继承的副作用 - 换句话说,它实际上并没有在最终化中做任何有用的事情.

应该在DataTable和DataSet对象上调用Dispose吗?包括MVP的一些解释:

system.data命名空间(ADONET)不包含非托管资源.因此,只要你没有添加一些特殊的东西,就没有必要处置它们.

了解Dispose方法和数据集?有权威Scott Allen的评论:

在实践中,我们很少处理DataSet,因为它提供的好处很少"

因此,一致认为目前没有充分的理由在DataSet上调用Dispose.

  • 提供的链接完全错过了DataTable是一种Finalizable对象的观点.请参阅下面的Nariman的答案. (5认同)
  • @Herman:他的答案更新以匹配DOK. (3认同)
  • @Willy 是的,这些绝对应该被处理掉,因为它们使用非托管资源。使用 `using` 块是显式调用还是隐式调用取决于您。 (2认同)

Nar*_*man 126

更新(2009年12月1日):

我想修改这个答案,并承认原来的答案是有缺陷的.

原始分析确实适用于需要最终确定的对象 - 并且如果没有准确,深入的理解,表面上不应接受的做法仍然存在.

但是,事实证明,DataSets,DataViews,DataTables 在其构造函数中抑制了最终化 - 这就是为什么在它们上面调用Dispose()显然什么也不做.

据推测,这是因为他们没有非托管资源; 因此,尽管MarshalByValueComponent允许非托管资源,但这些特定实现并不需要,因此可以放弃最终确定.

(.NET作者会注意抑制通常占用最多内存的类型的最终化,这说明了这种实践对于最终类型的重要性.)

尽管如此,自从.NET Framework(大约8年前)开始以来,这些细节仍然没有得到充分记录(这是非常令人惊讶的)(您基本上可以通过自己的设备筛选出相互冲突的,模糊不清的材料来将这些碎片放在一起有时令人沮丧,但确实提供了对我们每天依赖的框架的更完整的理解.

经过大量阅读,这是我的理解:

如果一个对象需要最终化,它可能占用内存的时间超过它需要的时间 - 这就是为什么:a)任何定义析构函数的类型(或从定义析构函数的类型继承)都被认为是可终结的; b)在分配时(在构造函数运行之前),指针放在Finalization队列上; c)最终目标通常需要回收2个集合(而不是标准1); d)抑制终结不会从终结队列中删除对象(由SOS中的!FinalizeQueue报告)此命令具有误导性; 知道最终化队列中的对象(在其中本身)是没有用的; 知道什么对象在终结队列中并且仍然需要最终化将是有帮助的(是否有一个命令?)

抑制终结在对象的头中稍微关闭,向运行时指示它不需要调用其Finalizer(不需要移动FReachable队列); 它保留在Finalization队列中(并继续由SOS中的!FinalizeQueue报告)

DataTable,DataSet,DataView类都以MarshalByValueComponent为根,这是一个可以(可能)处理非托管资源的可终结对象

  • 由于DataTable,DataSet,DataView不会引入非托管资源,因此它们会抑制其构造函数中的最终化
  • 虽然这是一种不寻常的模式,但它使调用者不必担心在使用后调用Dispose
  • 这可能是DataTables可能在不同的DataSet之间共享的事实,这可能是DataSets不关心处理子DataTables的原因.
  • 这也意味着这些对象将出现在SOS中的!FinalizeQueue下
  • 但是,在单个集合之后,这些对象仍然可以回收,就像它们的非最终对应物一样

4(新参考):

原答案:

对此有很多误导性和一般非常差的答案 - 任何落在这里的人都应该忽略噪音并仔细阅读下面的参考文献.

毫无疑问,应该在任何Finalizable对象上调用Dispose .

DataTables Finalizable.

调用Dispose可以显着加快内存的回收速度.

MarshalByValueComponent在其Dispose()中调用GC.SuppressFinalize(this) - 跳过这意味着在回收内存之前必须等待数十个(如果不是数百个)Gen0集合:

通过对最终化的基本理解,我们已经可以推导出一些非常重要的事情:

首先,需要完成的对象比没有完成的对象长寿.事实上,他们可以活得更久.例如,假设gen2中的对象需要最终确定.最终确定将被安排,但对象仍然在gen2中,因此在下一代gen2集合发生之前不会重新收集.这确实可能是一段很长的时间,事实上,如果事情进展顺利,那将是很长一段时间,因为gen2集合成本很高,因此我们希望它们很少发生.需要最终确定的旧对象可能必须等待几十个(如果不是数百个)gen0集合才能回收它们的空间.

其次,需要定型的物体会造成附带损害.由于内部对象指针必须保持有效,因此不仅直接需要最终化的对象会在内存中停留,而且对象所引用的所有内容也将直接或间接地保留在内存中.如果一个巨大的对象树被一个需要最终化的对象锚定,那么整个树都会徘徊,可能会像我们刚才讨论的那样长时间停留.因此,谨慎使用终结器并将它们放在具有尽可能少的内部对象指针的对象上是很重要的.在我刚给出的树例子中,您可以通过将需要完成的资源移动到单独的对象并在树的根中保持对该对象的引用来轻松避免该问题.通过适度的更改,只有一个对象(希望是一个漂亮的小对象)会停留,最终化成本最小化.

最后,需要完成的对象为终结器线程创建工作.如果你的终结过程是一个复杂的过程,那么唯一的终结者线程将花费大量时间来执行这些步骤,这可能导致工作积压,从而导致更多对象等待最终确定.因此,终结者尽可能少地工作是至关重要的.还要记住,虽然所有对象指针在最终确定期间仍然有效,但可能是这些指针导致已经完成的对象,因此可能不太有用.即使指针有效,最好避免在终结代码中遵循对象指针.安全,简短的终结代码路径是最好的.

从Gen2中看到100多个未引用的DataTables 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/

  • 声明"然而,事实证明,DataSets,DataViews,DataTables在其构造函数中抑制了最终化 - 这就是为什么在它们上面调用Dipose()显然什么都不做." 是一个不成功的人:这两个概念在很大程度上是无关的; 抑制终结的东西仍然可以在Dispose()中做一些事情.实际上,如果我们反转它实际上更有意义:Dispose()什么都不做,***这就是为什么***它会抑制构造函数中的最终化,即因为没有什么可做的它不想打扰它GC调用终结器(通常调用dispose). (13认同)

小智 23

您应该假设它执行了一些有用的操作并调用Dispose,即使它当前没有任何操作.NET Framework的版本,不能保证它将在未来版本中保持这种方式导致资源使用效率低下.

  • 假设它将始终实现IDisposable是相当安全的.添加或删除接口是一个重大变化,而更改Dispose的实现则不然. (28认同)
  • 此外,不同的提供程序可能具有实际执行IDisposable的实现. (5认同)

小智 16

即使对象没有非托管资源,处置也可能通过破坏对象图来帮助GC.通常,如果object实现了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);
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 的确。我最近看到一些代码,其中在一个非常大的循环中创建了许多数据表而没有被处理。这导致计算机上的所有内存都被消耗,并且进程因内存不足而崩溃。在我告诉开发人员对 DataTable 调用 dispose 后,问题就消失了。 (2认同)

Mic*_*tum 7

你自己创建DataTables吗?因为通常不需要遍历任何Object的子节点(如在DataSet.Tables中),因为它是Parent的工作来处置它的所有子成员.

通常,规则是:如果您创建了它并且它实现了IDisposable,请将其公开.如果你没有创建它,那么不要处置它,这是父对象的工作.但每个对象可能都有特殊规则,请查看文档.

对于.net 3.5,它明确地说"不再使用时处理它",这就是我要做的.

  • 根据我的理解,普遍的共识是对象应该处置自己的非托管资源.但是,IDisposable对象的集合通常不会遍历其元素来处理每个元素,因为在集合之外可能还有对其元素的其他引用:http://stackoverflow.com/questions/496722/if-a-通用信息收集被实例化到包含-的IDisposable项-DO的项目 (4认同)

Chu*_*way 6

我可以在对象实现IDisposeable的任何时候调用dispose.这是有原因的.

DataSet可能是巨大的内存耗尽.他们越早被标记为清理就越好.

更新

我回答这个问题已经5年了.我仍然同意我的回答.如果存在dispose方法,则在完成对象时应调用它.IDispose接口的实现有一个原因.

  • 调用dispose不会加速回收内存,为此你必须手动启动垃圾收集器,这通常是一个糟糕的计划. (5认同)
  • IDisposable的*primary*使用是释放非托管资源.通常,它还以对处理的实例有意义的方式修改状态.(即属性设置为false,引用设置为null等) (4认同)
  • 如果对象上有一个dispose方法,那么无论是否用于清理非托管对象,它都会被放在那里. (3认同)
  • 如果Dispose将一堆引用设置为null,则可能导致对象成为可能被跳过的集合的候选对象. (2认同)
  • Dispose 的重点不是清除托管对象的内存 - 这是垃圾收集器的工作。重点是清除非托管对象。似乎有证据表明数据集没有任何非托管引用,因此理论上不需要调用它们。话虽如此,我从来没有遇到过必须特意调用 Dispose 的情况 - 无论如何我都会调用它。 (2认同)

Sri*_*ddi 5

如果您的意图或此问题的上下文确实是垃圾收集,那么您可以将数据集和数据表显式设置为 null 或使用关键字 using 并让它们超出范围。Dispose 并没有像 Tetraeutron 之前所说的那样做太多。GC 将收集不再引用的数据集对象以及超出范围的数据集对象。

我真的希望 SO 强迫人们在拒绝投票之前实际写评论。

  • 否决投票,绝不禁止人们考虑不同的观点。 (2认同)