【问题标题】:Should I Dispose() DataSet and DataTable?我应该 Dispose() DataSet 和 DataTable 吗?
【发布时间】:2011-09-13 15:00:24
【问题描述】:

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

但是,从我目前所读到的内容来看,DataSet 和 DataTable 实际上没有任何非托管资源,因此 Dispose() 实际上并没有做太多事情。

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

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

那么,在我的所有数据集和数据表上调用 Dispose() 是否值得?

附录:

对于那些认为应该处置 DataSet 的人: 一般来说,disposing 的模式是使用usingtry..finally,因为你要保证 Dispose() 会被调用。

但是,对于一个集合来说,这会变得非常难看。例如,如果对 Dispose() 的调用之一引发异常,您会怎么做?你吞下它(这是“坏”),以便你可以继续处理下一个元素吗?

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

【问题讨论】:

  • Dispose 不应该抛出任何异常。如果是这样——写得不好,所以……试试 { some.Dispose(); } catch {} 应该足够了。 - blogs.msdn.com/b/clyon/archive/2004/09/23/233464.aspx
  • 它引入了很多混淆,毕竟不需要 Dispose() 实现。我只希望这在 MSDN 上有更好的记录,正是因为这些类的异常特性。
  • 严格来说,它不是必需的。就在此时,没有非托管资源。其他实现可能会引入它们。由您决定什么是最好的,但要知道Dispose 很可能是最差在 MS 实现上无操作。
  • 我注意到我的一个使用大量 DataSet 对象的应用程序存在明显的内存泄漏。我没有调用 .Dispose() 或对这些对象使用“使用”块。所以,我浏览了代码,并在我创建 DataSet 或 DataTable 的每个地方添加了一个“使用”块,瞧,内存现在被释放了。在我看来,.Dispose() 实际上对于 DataSet 和 DataTable 是必需的。

标签: datatable dataset dispose idisposable using


【解决方案1】:

这里有几个讨论解释了为什么 DataSet 不需要 Dispose。

To Dispose or Not to Dispose ?:

DataSet 中的 Dispose 方法的存在只是因为继承的副作用——换句话说,它实际上并没有在终结中做任何有用的事情。

Should Dispose be called on DataTable and DataSet objects? 包含来自 MVP 的一些解释:

system.data 命名空间 (ADONET) 不包含 非托管资源。因此,没有必要将其中任何一个处置为 只要你没有给自己添加一些特别的东西。

Understanding the Dispose method and datasets? 有权威人士 Scott Allen 的评论:

在实践中,我们很少 Dispose 数据集,因为它几乎没有什么好处”

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

【讨论】:

  • 提供的链接完全忽略了 DataTable 是一种 Finalizable 对象的观点。请参阅下面 Nariman 的回答。
  • 有趣的答案,但是 SqlConnection、SqlCommand 和 SqlDataAdapter 应该显式调用 Dispose 吗?
  • @Willy 是的,它们绝对应该被处置,因为它们使用非托管资源。是否使用 using 块显式或隐式调用它取决于您。
【解决方案2】:

更新(2009 年 12 月 1 日):

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

原始分析确实适用于需要最终确定的对象 - 并且在没有准确、深入的理解的情况下不应该在表面上接受实践这一点站立。

然而,事实证明 DataSets、DataViews、DataTables 在它们的构造函数中抑制了终结——这就是为什么在它们上显式调用 Dispose() 什么都不做的原因。

大概是因为它们没有非托管资源;因此,尽管 MarshalByValueComponent 允许使用非托管资源,但这些特定的实现不需要,因此可以放弃最终确定。

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

尽管如此,自从 .NET 框架(大约 8 年前)问世以来,这些细节仍然没有被充分记录,这非常令人惊讶(您基本上只能靠自己的设备来筛选虽然相互冲突、模棱两可的材料)拼凑在一起有时令人沮丧,但确实提供了对我们日常所依赖的框架的更完整的理解)。

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

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

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

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

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

4(新参考):

原答案:

对此有很多误导性且通常非常糟糕的答案 - 任何来到这里的人都应该忽略噪音并仔细阅读下面的参考资料。

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

数据表可终结的。

调用 Dispose 显着加速了内存的回收。

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

有了对最终确定的基本了解,我们 已经可以推断出一些非常重要的 事情:

首先,需要完成的对象 比没有寿命的物体寿命更长。 事实上,他们可以活得更久。 例如,假设一个对象 在 gen2 中需要最终确定。 定稿将被安排,但 对象仍在 gen2 中,所以它会 直到下一次才能重新收集 gen2 收集发生。那可能是 确实很长一段时间,事实上, 如果事情进展顺利,这将是一个 很长一段时间,因为 gen2 集合 是昂贵的,因此我们希望他们 很少发生。年长者 需要完成的对象可能 如果没有,必须等待几十个 之前的数百个 gen0 集合 他们的空间被回收了。

二、需要终结的对象 造成附带损害。由于 内部对象指针必须保留 有效,不仅将对象 直接需要finalization linger 在内存中,但一切都是对象 直接和间接地指, 也会留在记忆中。如果一个巨大的 对象树由 a 锚定 需要的单个对象 完成,然后是整个树 会徘徊,可能会持续很长时间 我们刚才讨论的时间。它是 因此使用终结器很重要 谨慎地将它们放在物体上 具有尽可能少的内部对象 尽可能多的指针。在树上 我刚刚给出的例子,你可以很容易地 通过移动来避免问题 需要最终确定的资源 单独的对象并保留一个 在根目录中引用该对象 树的。伴随着那适度的改变 只有一个对象(希望是一个不错的 小物体)会流连忘返 最终化成本最小化。

最后,需要完成的对象 为终结器线程创建工作。 如果您的最终确定过程是 复杂的,唯一的 终结器线程将花费 很多时间执行这些步骤, 这可能会导致工作积压 因此导致更多的物体逗留 等待最终确定。所以, 至关重要的是 终结者所做的工作尽可能少 可能的。还要记住,虽然 所有对象指针保持有效 在最终确定期间,它可能是 这些指针导致的情况 已经存在的对象 最终确定,因此可能会更少 比有用。通常最安全的是 避免跟随对象指针 完成代码,即使 指针有效。一个安全、短 终结代码路径是最好的。

从在 Gen2 中看到 100 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-collector-performance-using-finalizedispose-pattern.aspx

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

【讨论】:

  • 好点。当您有一个包含许多 DataTable 的 DataSet 时,您通常如何构建您的代码?大量嵌套的 using 语句?一次尝试..最终一次将其全部清理干净?
  • 声明“但是,事实证明,DataSets、DataViews、DataTables 在其构造函数中抑制了终结——这就是为什么在它们上显式调用 Dipose() 没有任何作用。”是不合逻辑的:这两个概念在很大程度上是不相关的;抑制终结的东西仍然可以在 Dispose() 中做一些事情。事实上,如果我们反转它实际上更有意义:Dispose() 什么都不做,这就是为什么它抑制构造函数中的终结,即因为没有什么可做它不想通过调用终结器(通常调用 dispose)来打扰 GC。
  • 谢谢。这个讨论是否也适用于TableAdapters?
【解决方案3】:

您应该假设它做了一些有用的事情并调用 Dispose,即使它在当前的 .NET Framework 版本中什么都不做。无法保证它会在未来的版本中保持这种状态,从而导致资源使用效率低下。

【讨论】:

  • 也不能保证它会在未来实现 IDisposable。如果它像 using(...) 一样简单,我会同意你的看法,但在 DataSet 的情况下,这似乎很麻烦。
  • 假设它总是实现 IDisposable 是相当安全的。添加或删除接口是一项重大更改,而更改 Dispose 的实现则不是。
  • 另外,不同的提供者可能有一个实际使用 IDisposable 做某事的实现。
  • 更不用说 DataTable 不是密封的 - 当你在做 new DataTable 时没什么大不了的,但在将 DataTable 作为参数或作为结果时非常重要方法调用。
【解决方案4】:

即使一个对象没有非托管资源,disposing 也可能通过破坏对象图来帮助 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);
                }
            }
        }
    }
}

【讨论】:

  • 确实如此。我最近看到了一些代码,其中很多 DataTables 是在一个非常大的循环中创建的,而没有被 Disposed。这会导致计算机上的所有内存都被消耗,并且进程在内存不足时崩溃。在我告诉开发人员在 DataTable 上调用 dispose 后,问题就消失了。
【解决方案5】:

只要对象实现 IDisposeable,我就会调用 dispose。它的存在是有原因的。

DataSet 可能会占用大量内存。越早标记为清理越好。

更新

我回答这个问题已经 5 年了。我还是同意我的回答。如果有一个 dispose 方法,则应该在您处理完对象后调用它。实现 IDispose 接口是有原因的。

【讨论】:

  • 调用 dispose 并不能加快内存的回收速度,要做到这一点,您必须手动启动垃圾收集器,这通常是一个糟糕的计划。
  • 如果 Dispose 将一堆引用设置为 null,它可能会导致对象成为可能被跳过的集合的候选对象。
  • Dispose 的重点不是清理托管对象的内存——这是垃圾收集器的工作。重点是清除非托管对象。似乎有证据表明 DataSet 没有任何非托管引用,因此理论上不需要对它们进行处置。话虽如此,我从来没有遇到过不得不特意打电话给 Dispose 的情况——无论如何我都会打电话给它。
  • IDisposable 的主要用途是释放非托管资源。通常,它还会以对已处置实例有意义的方式修改状态。 (即属性设置为 false,引用设置为 null 等)
  • 如果一个对象上有一个 dispose 方法,它被放在那里是有原因的,不管它是否用于清理非托管对象。
【解决方案6】:

您自己创建数据表吗?因为通常不需要遍历任何对象的子对象(如在 DataSet.Tables 中),因为处理其所有子成员是父对象的工作。

一般来说,规则是:如果您创建了它并且它实现了 IDisposable,则将其丢弃。如果您没有创建它,则不要处置它,这是父对象的工作。但是每个对象可能有特殊的规则,请查看文档。

对于 .NET 3.5,它明确表示“不再使用时将其丢弃”,这就是我要做的。

【讨论】:

  • 据我了解,普遍的共识是对象应该处置自己的非托管资源。但是,IDisposable 对象的集合通常不会遍历其元素来处理每个元素,因为在集合之外可能还有对其元素的其他引用:stackoverflow.com/questions/496722/…
  • 没错,集合总是我认为特别的东西,因为它们通常不“做”任何事情,它们只是......容器,所以我从不为此烦恼。
【解决方案7】:

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

我真的希望 SO 迫使人们在否决答案之前实际写评论。

【讨论】:

  • + 1 我猜有些人不想让其他人考虑不同的观点。
  • 投反对票,绝不允许人们考虑不同的观点。
【解决方案8】:

数据集通过实现 IDisposable 的 MarshalByValueComponent 实现 IDisposable。由于数据集是受管理的,因此调用 dispose 并没有真正的好处。

【讨论】:

  • 现在可以了,谁知道以后会怎样。
  • 这种你推测任何代码将来都不会做它应该做的事情的态度对所有相关人员来说都是一种痛苦。
【解决方案9】:

尝试使用 Clear() 函数。 它非常适合我处理。

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

【讨论】:

    【解决方案10】:

    无需 Dispose() 因为DataSet继承了MarshalByValueComponent类,而MarshalByValueComponent实现了IDisposable接口

    【讨论】:

      【解决方案11】:

      这是正确处置DataTable的正确方法。

      private DataTable CreateSchema_Table()
      {
          DataTable td = null;
          try
          {
              td = new DataTable();
              //use table DataTable here
              
              return td.Copy();
          }
          catch {  }
          finally
          {
              if (td != null)
              {
                  td.Constraints.Clear();
                  td.Clear();
                  td.Dispose();
                  td = null;
              }
          }
      }
      

      【讨论】:

        【解决方案12】:

        这可能是处置和释放DataSet 消耗的内存的最佳/正确方式。

        try
            {
                DataSet ds = new DataSet("DS");
                //use table DataTable here
                
            }
            catch {  }
            finally
            {
                if (ds != null)
                        {
                            ds.EnforceConstraints = false;
                            ds.Relations.Clear();
                            int totalCount = ds.Tables.Count;
        
                            for (int i = totalCount - 1; i >= 0; i--)
                            {
                                DataTable td1 = ds.Tables[i];
                                if (td1 != null)
                                {
                                    td1.Constraints.Clear();
                                    td1.Clear();
                                    td1.Dispose();
                                    td1 = null;
                                }
                            }
        
                            ds.Tables.Clear();
                            ds.Dispose();
                            ds = null;
                        }
            }
        

        【讨论】:

          猜你喜欢
          • 2011-07-29
          • 2010-11-17
          • 2011-11-23
          • 2015-09-07
          • 1970-01-01
          • 2010-10-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多