垃圾收集器的作用是什么?

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还有什么意义?难道不是为了程序员而存在的吗?


1
对于那些有兴趣优化他们的代码的人来说,这是一个好问题。 - Kon
3
这与优化代码有什么关系? - Scott Dorman
15个回答

15

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

另外,我想补充的是,使用“using”语句可以以优雅的方式为您完成这项工作。


7
垃圾回收是用来释放未使用内存的。将GC扩展到释放其他资源存在各种问题。其中一个问题是,终结器(当对象被垃圾回收时执行的方法)会使引用循环不可回收,除非您强制限制从终结器中取消引用,这使得它们非常棘手。另一个原因是,大多数资源需要在某种及时的方式下进行释放,而依赖于垃圾回收则无法实现。还有一个原因是,将GC限制为内存管理处理任何应用程序中的大部分资源管理,以及几乎所有非常规的资源管理。其他资源通常足够有趣,需要额外的代码来明确它们如何释放。另一个原因是,在某些应用程序中,GC使应用程序运行更快,因为它减少了复制的数量,以满足所有权语义。这对于其他资源并不是一个问题。其他人可以像这样继续讲上数小时。

6

但是如果你必须手动处理垃圾回收,那么GC的意义在哪里?!难道不是GC应该为程序员处理这个问题吗?

问题在于你无法确定GC何时运行。如果你的应用程序从未对内存施加压力,它可能根本不会运行。


2
...而且有些资源比内存更为稀缺。 - Aaron Maenpaa

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的方法。

4

实现IDisposable接口的对象试图告诉你它们与非托管内存中的结构有链接。垃圾回收器为了提高效率而批量运行,但这意味着可能需要一段时间才能处理掉你的对象,这意味着你会比应该持有资源的时间更长,这可能会对性能/可靠性/可扩展性产生负面影响。


3
GC(垃圾回收器)会定期运行,负责内存管理,让一切保持整洁。你可能认为像你发布的代码片段那样的内容毫无用处,但实际上,它经常帮你省去很多麻烦(想想 C/C++ 手动内存管理),因为它大大减少了内存泄漏的风险,让你只需关注应用程序如何运行,而不是如何管理内存。释放文件句柄和数据库连接可以提高效率,因为垃圾回收是非确定性的,可能不会立即发生,你不希望这些文件句柄和打开的数据库连接影响系统性能。顺便说一下,你的代码真的很丑,我总是推荐使用 using 语句,并经常像这样编写我的数据库代码:
using (SqlConnection connection = new SqlConnection(...))
using (SqlCommand command = connection.CreateCommand())
{
   ...
}

当连接和命令对象超出作用域时(即执行离开块时),这将自动调用dispose函数。


1
C++ 手动内存管理?兄弟,那太 80 年代了,你从没听说过 RAII 吗? - gbjbaanb

2

因为垃圾回收并不是最高效的 - 它并不总是在资源不再使用时立即发生。所以,当您处理非托管资源,如文件 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块中进行处理。


1
如果您正在使用using语句,那么您不需要显式调用Dipose()方法,这就是使用'using'的全部意义。 - IAmCodeMonkey
我并不会说垃圾回收机制不高效,因为它并不会在资源不再使用时立即发生。垃圾回收运行时的一部分是非确定性的,并且只有在确定需要运行时才会运行。 - Scott Dorman
@Scott:没错。能够延迟垃圾回收实际上使其更加高效。 - Jon Skeet
@Scott:你说得对,我只是没有用一个好的方式表达我的想法。@IAmCodeMonkey:我从来没有说过要明确调用Dispose()。我说过要在可处理的任何东西上使用'using'。 - Kon
垃圾回收器总体效率较低,因为它只是推迟了内存释放。然而,你还需要一个新的线程来进行垃圾回收,并且主应用程序需要被锁定(在垃圾回收运行时不能操作对象!),但是给对象添加终结器会使其真正低效。 - gbjbaanb

2
GC(垃圾回收器)的设计初衷并非管理资源,而是用于管理内存分配。在处理数据库连接等其他资源时,需要考虑除内存之外的其他资源。.NET平台可以使用“using”表达式和“IDisposable”接口等扩展来帮助避免内存泄漏。C++没有内置的垃圾回收机制,但可以使用某些形式的共享指针来帮助管理内存,同时RAII编程风格对管理其他类型的资源非常有帮助。在cPython中,有两种不同的垃圾回收系统。引用计数实现会在最后一个引用被删除时立即调用析构函数。对于常见的“堆栈”对象,这意味着它们会立即清理,就像C++ RAII对象的情况一样。缺点是如果存在引用循环,引用计数收集器将永远无法释放对象。因此,他们还有一个次要的非确定性垃圾回收器,类似于Java和.NET收集器。与.NET使用using语句一样,cPython也试图处理最常见的情况。因此,为了简化内存管理,非确定性垃圾回收可以用于处理其他资源,只要时间上不是问题,并且需要及时释放其他资源时,需要使用另一种机制(如小心编程、引用计数GC、using语句或真正的RAII对象)。

1

上述代码释放了已获取的资源(尽管我不认为您应该自己调用Dispose()方法,因为释放资源意味着关闭流等)。GC从内存中删除对象(释放对象使用的内存),但只有在对象释放资源后才能执行此操作。


1
你应该在使用完对象后一定要调用Dispose()方法(或者使用using语句,它实现了相同的功能)。 - Chris Marasti-Georg

1

我对C#不是很确定,但通常情况下,垃圾回收器会管理内存。除了对象内存之外,这个连接还有服务器资源。数据库在一个单独的进程中,必须维护连接。关闭操作可以清理它们。


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