检测垃圾回收器是否被调用(.Net)

5
当使用SqlConnection时,重要的是在使用完毕后始终将其关闭 - 可以通过.Close()或将SqlConnection放置在“using”中来实现。不幸的是,人们,包括我自己,在这方面往往会忘记,这就是垃圾收集器暂时帮助我,直到我忘记关闭连接太多次或使用应用程序的人数增加为止。
我想知道,如果可能的话,如何检测垃圾收集器是否处理了SqlConnection,因为它发现它不再使用,或者SqlConnection是否正确关闭。
另一种方法可能是继承SqlConnection,并在其初始化程序上放置一个计时器,并在处理类时检查连接关闭需要多长时间。我不太喜欢计时器,但这个想法只是在写这篇文章时出现的。
也许有第三种更聪明的方法...你会推荐什么?

不行不行不行!仅仅使用.Close()是不够的。这就是“using”结构的全部意义:太多人也忘记了你的.Close()必须在finally块中。因此,可以通过.Close()或将SqlConnection放置在“using”中来关闭连接。 - Joel Coehoorn
6个回答

4

由于SqlConnection是密封的,您将无法从它继承。(而且我认为这样做不是一个好主意--如果可能的话,您应该在Dispose(false)中添加代码,因为这是终结器调用的内容)。

更好的方法是使用静态代码分析工具来检测这些问题,该工具有能力检测您的代码中所有忘记释放连接的地方。Visual Studio内置了一个(链接1),或者您可以使用FxCop

为了帮助您不要忘记释放连接,建议您:

  • 将所有数据库连接代码放在一个层/程序集/模块中,以便它不会分散在项目中。
  • 编写实用程序方法来执行SQL命令并返回结果;这样你就不需要在更多的地方创建SQLConnection。
  • 记得利用C# using construct

2
最好的方法是找到根本原因并释放连接。+1 - Randy Levy

2

有一条经验法则是,“如果你必须考虑垃圾收集器,那么你可能做错了什么。”(当然,还有其他的方法...)

在我看来,确保连接被明确关闭或通过usingfinally块关闭是最好的方法。

显然,您已经了解了这些技术...所以也许只需要浏览一遍代码并进行可能的重构。


1
如果您的应用程序正在使用SQL连接池(默认情况下),那么这并不重要,因为连接会被重复使用,并且在调用.Close()或离开using() {}块时不会关闭。

http://msdn.microsoft.com/en-us/library/8xx3tyca.aspx

回答你的问题,我不认为有一个GC收集的事件,但即使有也无所谓,因为你永远不会知道GC是否选择在那个收集过程中回收你的对象(并非所有对象都因为分代算法而被清理)。
我还建议避免尝试使用定时器和“检查”,因为你可能会持有对该对象的引用,从而防止它被处理。

但这很重要,因为如果连接没有关闭,则它们不会返回到池中,因此它们将无法立即被重用。这可能会耗尽您的连接池或消耗宝贵的资源,从而导致问题。 - Randy Levy
1
同意,但重点是OP想知道垃圾回收发生的“时间”。我的观点是,使用池化时不会发生SQL连接的收集。唯一的解决方案是实际修复并确保调用.Close(),而不是依赖于GC收集事件或计时器检查。 - David

1

我对垃圾回收器不是很熟悉,也不知道是否有直接获取信息的方式,但我的第一个想法如下。

只需创建一个弱引用指向一个虚拟对象。如果以后查看弱引用时,该对象已经消失,你可以推断出进行了垃圾回收。

(此答案仅适用于检测垃圾回收运行情况。我完全忽略了这样做的原因 - 处理资源泄漏有更好的策略。)


这就是重点,你需要处置以防止终结。如果你只保留一个弱引用并检查是否发生了这种情况,那么你已经太迟了(垃圾回收在终结之后进行)。 - Jouke van der Maas
当然可以。正如最后一句所述,我只想回答“检测垃圾收集器是否被调用”。首先,您不应编写泄漏资源的代码。通过执行静态分析和运行时分析,可以有效地避免这种情况。 - Daniel Brückner

0

如果您忘记释放,对象将被终结。没有办法控制它发生的时间,也没有办法知道对象是否已经被终结。必须创建一个单独的线程来终结对象,因此会减慢应用程序的速度。这就是为什么您需要释放资源。在框架中实现IDisposable接口的所有类都会调用GC.SuppressFinalize方法,以便对象不会被终结。

您无法控制此行为。如果您的对象不再使用,它将自动被收集。您唯一能做的是调用GC.SuppressFinalize来停止这个过程,但我不建议这样做,因为如果您忘记了,那么您将会遭受终身的损失。

您可以创建一个包装类(而不是子类),在您的代码中使用它提供的一些简单方法来始终调用Dispose。否则,请确保检查得非常仔细。


我会说“通常会被最终确定”。很容易出现这样的情况,即未能“Dispose”与某些静态持续时间相关联的对象将防止该对象或任何它直接或间接引用的对象永远不会成为垃圾回收的候选对象。 - supercat

0

首先,如果您甚至考虑使用GC,那么您肯定有严重的问题。我建议您使用诸如模拟之类的技术对代码进行单元测试,以确保您的代码调用了Close。如果您的单元测试表明已经调用了Close,则您的代码是正确的,您不必做任何不必要的危险操作,比如搞乱垃圾回收器。

其次,如果您仍然坚持采用运行时检查路线,那么您唯一需要考虑的事情就是挂钩SqlConnectionStateChange事件。例如,当ConnectionState从Open变为Closed时,它将触发。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接