SqlConnection 会被 GC 处理掉吗?

发布于 2024-09-17 10:10:00 字数 806 浏览 1 评论 0原文

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

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

Disclaimer: I know IDisposable should be implemented when dealing with unmanaged resources. The rest of the code should be deterministic and do using (...) { } (equivalent of try {} finally { Dispose(); }) to guarantee a cleanup as soon as possible. Also, the GC will not call Dispose(), so the recommended pattern is to override the Finalize() method (in C# using the destructor syntax) which then calls Dispose(). The GC will usually call Finalize() (unless GC.SuppressFinalize() has been called).

Problem: So now that I got that out of the way, I have an odd scenario where I cannot do using (SqlConnection...) { } due to code out of my control. I can usually do a deterministic Dispose(), but can't guarantee it. I used Reflector to disassemble SqlConnection and see that it uses Dispose(), but unless I'm blind there is no finalizer/destructor (Finalize() or ~SqlConnection()). Does that mean that the GC will not "clean up" (send back to the pool) the connection in the odd case I can't? I haven't been able to find anything definitive...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

仅此而已 2024-09-24 10:10:00

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

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

但请记住,SqlConnection 是“真实”连接的包装器。事实上,它很可能是一组不断变化的对象的包装器,这些对象代表不同的连接状态。这是允许有效地池化“真实”连接的机制的一部分,因为每次调用 Open() 时,它都会从池中获取相关对象,并且每次调用 >Close()(无论是直接通过 Dispose() 还是通过离开 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 标志以便关闭(或处置)读取器来关闭连接吗?后一种情况是我记得的唯一一种必须让连接离开未处理范围的情况。

Well, it won't get disposed, as finalisation is not disposal.

There is a finaliser in System.ComponentModel.Component, but its suppressed in SQLConnection's constructor. This is a good idea if you inherit from something with a finaliser that you know with 100% certainty you won't need, but a bad idea otherwise. In this case it's a good idea.

Remember though, that SqlConnection is a wrapper on a "real" connection. Indeed, it's most likely a wrapper on a changing set of objects which represent different connection states. This is part of the mechanism that allows for the "real" connection to be pooled efficiently, as each time you call Open() it obtains the relevant object from the pool, and every time you call Close() (whether directly, by Dispose() or by leaving the scope of a using) it returns it.

Now, remember that only objects that directly hold an unmanaged resource or something otherwise not the GC's concern, need to be finalised. SqlConnection holds an object that may (depending on the state of SqlConnection) be one that holds an unmanaged resource (or indeed, deeper through the nest of classes). There is therefore no need for SqlConnection to itself be finalised. Consider the three possible ways an open SqlConnection can stop being an open SqlConnection:

  1. Close() is called. This immediately returns the real connection to the pool (or closes it if there is no pooling).
  2. Dispose() is called. This calls Close() with the same effect.
  3. The object gets garbage collected.

Now, in the third case, the object holds a reference to the object that has the real connection. It also is the only object that does so. That object is hence also going to be garbage collected. If it has a finaliser (which it probably does, though I'm not going to assume there aren't further clever tricks going on) then that finaliser will cause it to be placed into the finaliser queue, and it will be eventually finalised.

If SqlConnection had a finaliser, the only real effects would be:

  1. Potential for buggy code (dealing with finalisable members in finaliser code is fraught, as you don't know whether or not they've been finalised).
  2. Potential for slowing things up (the real connection is going to be finalised anyway, at best we're just slowing up the finalisation and GC).
  3. Nothing to do here anyway (the real connection will be finalised without any help here).

So, putting a finaliser on the SqlConnection is a lose without a win. Also, your real connection should hopefully be eventually finalised.

This said, it's still far from ideal and still very likely to leak connections. Could you detail precisely why you can't call Close() or dispose yourself? Could the code managing the connection not call close for you (the object should end its days somewhere, and should be closed there)?

Do you need to keep it alive for an IDataReader or an object that feeds from an IDataReader to be allowed to complete? In which case, could you use the CommandBehavior.CloseConnection flag so that closing (or disposing of) the reader closes the connection? This latter case is about the only case where I can recall ever having to let a connection leave scope un-disposed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文