3
SqlConnection connection = new SqlConnection(FROM_CONFIGURATION) 
SqlCommand command = new SqlCommand("SomeSQL", connection); 
connection.Open(); 
command.ExecuteNonQuery(); 
command.Dispose(); 
connection.Dispose();

建议上面的代码应该包含try/catch(或using),这样如果抛出异常,所有资源都会被正确处理。

但是如果你不得不担心手动处理东西,那么 GC 的意义何在?!GC 不是为编码员处理这个问题吗?

4

15 回答 15

15

正如这里的其他人所说,GC 是不确定的,所以你不知道什么时候会收集你的对象。我想澄清的是,这不是内存的问题,而是系统资源(打开的文件、数据库连接)的问题,这些资源很昂贵,应该尽快释放。当您知道不再使用连接时,Dispose 允许您执行此操作。如果它们没有及时释放,系统可能会耗尽这些资源,而 GC 不会意识到这一点。这就是为什么您必须手动执行此操作的原因。

另外我想补充一点,使用“使用”语句会以一种很好的方式为您完成。

于 2008-10-21T19:46:49.707 回答
7

垃圾收集在这里负责释放未使用的内存

扩展 GC 以释放其他资源存在问题的原因有多种。其中之一是终结器(在对象被垃圾回收时执行的方法)使引用周期无法回收,除非您强烈限制对终结器的取消引用,这使得它们使用起来非常棘手。

另一个原因是大多数资源需要以某种及时的方式释放,这在依赖垃圾收集时是不可能的。

还有一个原因是,将 GC 限制在内存管理上会处理任何应用程序中的大量资源管理,以及几乎所有不感兴趣的资源管理。其他资源通常很有趣,值得一些额外的代码来明确说明它们是如何发布的。

另一个原因是,在某些应用程序中,GC 使应用程序运行得更快,因为它减少了为满足所有权语义而完成的复制量。这与其他资源无关。

其他人可以这样持续几个小时。

于 2008-10-21T19:48:51.120 回答
6

但是如果你不得不担心手动处理东西,那么 GC 的意义何在?!GC 不是为编码员处理这个问题吗?

问题是您不知道GC何时运行。如果您的应用程序从不给内存施加压力,它可能根本无法运行。

于 2008-10-21T19:40:40.747 回答
5

假设我有这个代码:

class MonkeyGrabber : IDisposable {
   public MonkeyGrabber() { /* construction grabs a real, live monkey from the cage */
   public void Dispose() { Dispose(true); /* releases the monkey back into the cage */ }
   // the rest of the monkey grabbing is left as an exercise to grad student drones
}

class MonkeyMonitor {
    public void CheckMonkeys() {
        if (_monkeyPool.GettingTooRowdy()) {
            MonkeyGrabber grabber = new MonkeyGrabber();
            grabber.Spank();
        }
    }
}

现在,我的 MonkeyMonitor 会检查猴子,如果它们太吵闹,它会获取宝贵的系统资源 - 连接到系统的单个猴子抓爪,并用它抓住猴子并打它。由于我没有处理它,猴爪仍然抓着猴子在休息笼上方晃来晃去。如果其余的猴子继续吵闹,我就无法制作新的 MonkeyGrabber,因为它仍然在帮助。哎呀。一个人为的例子,但你明白了:实现 IDisposable 的对象可能会占用应该及时释放的有限资源。GC 最终可能会放手,也可能不会放手。

另外,有些资源需要及时释放。我有一组类,如果它们在应用程序退出之前没有被应用程序或 GC 处理,将导致应用程序严重崩溃,因为它们来自的非托管资源管理器在 GC 处理它时已经消失了.

更多关于 IDisposable

using 是你的朋友——它是迄今为止我们最接近RAII的。

于 2008-10-21T19:55:57.080 回答
4

实现 IDisposable 的对象试图告诉您它们与非托管内存的结构有链接。垃圾收集器分批运行以提高效率。但这意味着可能需要一段时间才能处理您的对象,这意味着您持有资源的时间将超过应有的时间,这可能会对性能/可靠性/可扩展性产生负面影响。

于 2008-10-21T19:44:16.573 回答
3

GC 偶尔运行并负责内存管理,为您保持一切整洁。当您看到像您发布的代码片段时,您可能会认为它没有用,但通常情况下,它为您省去了很多麻烦(想想 C/C++ 手动内存管理),因为它大大减少了内存泄漏并让您担心关于您的应用程序将如何运行,而不是您将如何管理内存。处理文件句柄和数据库连接是一种提高效率的方法,因为垃圾收集不是确定性的,可能不会立即发生,并且您不希望这些文件句柄和打开的数据库连接削弱系统性能。顺便说一句,你的代码真的很丑,我一直提倡 using 语句,经常这样写我的 db 代码:

using (SqlConnection connection = new SqlConnection(...))
using (SqlCommand command = connection.CreateCommand())
{
   ...
}

当连接和命令对象超出范围时,即执行离开块时,这会自动调用连接和命令对象。

于 2008-10-21T19:47:38.893 回答
2

因为 GC 不是最有效的——它并不总是在资源不再使用时立即发生。因此,当您处理文件 I/O、数据库连接等非托管资源时,最好在等待并依靠 GC 处理之前释放/清理这些资源。

并考虑使用using关键字:

using (SqlConnection connection = new SqlConnection(FROM_CONFIGURATION))
using (SqlCommand command = new SqlCommand("SomeSQL", connection))
{
  connection.Open(); 
  command.ExecuteNonQuery(); 
  command.Dispose(); 
  connection.Dispose();
}

此外,作为一般规则,任何可以处理的东西都应该放在一个using块中。

于 2008-10-21T19:43:31.910 回答
2

... GC 从来都不是用来管理资源的;它旨在管理内存分配......在数据库连接的特定情况下,您正在处理的不仅仅是内存......(Scott Dorman)

OP 没有标记特定平台,尽管大多数答案都是特定于 .net 的,并指出 GC 主要用于避免内存泄漏,但using表达式等扩展IDisposable可以提供很大帮助。

其他平台提供其他解决方案。例如,在 C++ 中,没有(内置)垃圾收集,但某些形式的共享指针可用于帮助内存管理,而 RAII 风格的编码在管理其他类型的资源时非常有帮助。

在 cPython 中,使用了两种不同的垃圾收集系统。当最后一个引用被删除时,引用计数实现立即调用析构函数。对于常见的“堆栈”对象,这意味着它们会立即被清理,就像 C++ RAII 对象所发生的一样。缺点是如果你有一个引用循环,引用计数收集器永远不会处理对象。因此,它们有一个辅助的非确定性垃圾收集器,其工作方式类似于 Java 和 .NET 收集器。与使用 using 语句的 .NET 一样,cPython 尝试处理最常见的情况。

因此,为了回答 OP,非确定性垃圾收集有助于简化内存管理,它也可以用于处理其他资源,只要及时性不是问题,以及另一种机制(如仔细编程、引用计数 GC、在需要及时释放其他资源时,需要使用 using 语句或真实的 RAII 对象。

于 2008-10-21T20:01:40.703 回答
1

啊,我现在明白了。我将内存管理与非托管资源管理混淆了。感谢您的澄清!

于 2008-10-22T03:35:13.973 回答
1

上述代码释放获取的资源(尽管我不认为您应该自己调用 Dispose() 方法,释放资源意味着关闭流和类似的东西)。GC 从内存中删除对象(释放对象使用的内存),但只有在对象释放资源后才能完成。

于 2008-10-21T19:40:09.063 回答
1

我不太确定 c#,这就是它的样子,但通常,垃圾收集器管理内存。除了对象内存之外,此连接还具有服务器资源。处于单独进程中的数据库必须保持连接。关闭清理这些。

于 2008-10-21T19:41:18.967 回答
1

.NET 中的垃圾收集器 (GC) 是 .NET 公共语言运行时 (CLR) 的核心部分,适用于所有 .NET 编程语言。GC 从来都不是用来管理资源的。它旨在管理内存分配,并且在管理直接分配给本机 .NET 对象的内存方面做得非常出色。它并非旨在处理非托管内存和操作系统分配的内存,因此管理这些资源成为开发人员的责任。

在数据库连接的特定情况下,您正在处理的资源不仅仅是内存 - 特别是连接池、可能的隐式事务范围等。通过调用 Close() 和/或 Dispose() 您明确告诉对象释放那些非托管资源立即被托管,而托管资源将等待一个 GC 周期发生。

于 2008-10-21T19:47:51.870 回答
0

GC 确实会处理对象,但处理可能不会立即发生。手动处理对象将更快地释放内存。

于 2008-10-21T19:41:24.130 回答
0

GC 在释放外部资源(例如数据库连接或文件句柄)时受到限制。但是,为了在 .NET 世界中分配内存,它需要处理许多普通的内存管理任务。

于 2008-10-21T19:42:59.480 回答
0

上面提供了一个示例,说明当本地分配的内存或资源作为句柄进入托管世界时。在这种情况下,因为托管世界没有分配内存,所以它不能“自动整理”它。内存/资源必须显式地配置或至少配置在终结器中。

但是,在绝大多数情况下,尤其是在谈论对大多数公司核心目标(是的业务逻辑)至关重要的代码时,您不必担心这种事情,更少的代码意味着更少的错误。

于 2008-10-21T19:49:07.157 回答