【问题标题】:Will SqlConnection get disposed by GC?SqlConnection 会被 GC 处理掉吗?
【发布时间】:2010-09-04 00:42:28
【问题描述】:

免责声明:我知道在处理非托管资源时应该实现IDisposable。其余代码应该是确定性的,并执行using (...) { }(相当于try {} finally { Dispose(); })以保证尽快清理。此外,GC 将 not call Dispose(),因此推荐的模式是覆盖 Finalize() 方法(在 C# 中使用析构函数语法),然后调用 Dispose()。 GC 通常会调用Finalize()(除非已经调用了GC.SuppressFinalize())。

问题:所以现在我解决了这个问题,我遇到了一个奇怪的情况,由于我无法控制代码,我无法执行 using (SqlConnection...) { }。我通常可以做一个确定性的Dispose(),但不能保证。我使用 Reflector 反汇编 SqlConnection 并看到它使用了 Dispose(),但除非我是盲人,否则没有终结器/析构函数(Finalize()~SqlConnection())。这是否意味着在我不能的奇怪情况下,GC 不会“清理”(发送回池)连接?我一直没能找到任何确定的...

【问题讨论】:

    标签: .net idisposable sqlconnection finalizer


    【解决方案1】:

    好吧,它不会被处置,因为最终确定不是处置。

    System.ComponentModel.Component 中有一个终结器,但它在SQLConnection 的构造函数中被抑制。如果您从具有终结器的东西继承,这是一个好主意,您知道 100% 确定您不需要,但否则是个坏主意。在这种情况下,这是个好主意。

    但请记住,SqlConnection 是“真实”连接的包装器。实际上,它很可能是一组变化的对象的包装,这些对象代表不同的连接状态。这是允许“真实”连接被有效池化的机制的一部分,因为每次调用Open() 它都会从池中获取相关对象,并且每次调用Close()(无论是直接调用,还是通过@ 987654327@ 或离开using 的范围,它会返回它。

    现在,请记住,只有直接持有非托管资源或其他与 GC 无关的对象才需要最终确定。 SqlConnection 拥有一个对象,该对象可能(取决于SqlConnection 的状态)是一个拥有非托管资源的对象(或者实际上,更深层次的类嵌套)。因此,SqlConnection 本身无需最终确定。考虑开放SqlConnection 不再是开放SqlConnection 的三种可能方式:

    1. Close() 被调用。这会立即返回到池的真实连接(如果没有池,则将其关闭)。
    2. Dispose() 被调用。这调用Close() 具有相同的效果。
    3. 对象被垃圾回收。

    现在,在第三种情况下,对象持有对具有实际连接的对象的引用。它也是唯一这样做的对象。因此,该对象也将被垃圾收集。如果它有一个终结器(它可能有,尽管我不会假设没有更多聪明的技巧在进行),那么终结器将导致它被放入终结器队列中,并且最终会被终结。

    如果SqlConnection 有一个终结器,唯一真正的效果是:

    1. 存在错误代码的可能性(处理终结器代码中的可终结成员令人担忧,因为您不知道它们是否已经终结)。
    2. 可能会减慢速度(无论如何,真正的连接都会最终确定,我们充其量只是减慢最终确定和 GC 的速度)。
    3. 无论如何这里都无事可做(真正的连接将在此处完成,无需任何帮助)。

    因此,在SqlConnection 上放置一个终结者是没有胜利的失败。此外,您的真正联系应该有望最终确定。

    也就是说,它仍然远非理想,并且仍然非常可能会泄漏连接。您能否详细说明为什么您不能致电Close() 或自行处置?管理连接的代码不能为您调用 close(对象应该在某处结束它的日子,并且应该在那里关闭)吗?

    您是否需要保持它处于活动状态才能让IDataReader 或从IDataReader 提供的对象被允许完成?在这种情况下,您可以使用CommandBehavior.CloseConnection 标志以便关闭(或处置)阅读器关闭连接吗?后一种情况是我记得的唯一一种不得不让连接离开作用域的情况。

    【讨论】:

    • “SqlConnection 拥有一个对象,该对象可能(取决于 SqlConnection 的状态)是一个拥有非托管资源的对象” - 好点,我没有验证 SqlConnection 实际上拥有非托管资源。基本上,管理连接的代码是一个没有正确实现的外部库。我想我毕竟可以对其进行更改并完全避免这个问题。
    • 很高兴听到这个消息。在正确的对象上完成最终确定的事情一切都很好,但是如果最终确定没有足够频繁地发生(而且 IME 几乎从不这样做 - 或者更糟糕,更糟糕的是,它在测试中而不是在直播)。
    • +1 好答案。我对 msdn 上的以下声明有点困惑,这表明 SqlConnection 是托管资源。 “不要在类的 Finalize 方法中对 Connection、DataReader 或任何其他 托管 对象调用 Close 或 Dispose。”来自HERE
    • @TimSchmelter SqlConnection 类确实是一个托管对象,它包装了非托管对象(通过一些托管的间接级别),它执行 1 和 0 的实际传输。是的,永远不要在终结器中处理(除非您知道托管对象应该有自己的终结器但没有自己的终结器)。通常这样做会弄乱该对象的终结器,并且由于该其他对象中的终结器,因此也应该是不必要的。
    猜你喜欢
    • 1970-01-01
    • 2013-03-16
    • 2011-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多