什么是垃圾收集器的意义

时间:2008-10-21 19:38:27

标签: .net garbage-collection

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在那里为编码器照顾这个?

15 个答案:

答案 0 :(得分:15)

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

另外我想补充一点,使用'using'语句会很好地为你做。

答案 1 :(得分:7)

垃圾收集用于处理释放未使用的内存

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

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

另一个原因是限制GC到内存管理处理任何应用程序中的大量资源管理,以及几乎所有非感兴趣的资源管理。其他资源通常足够有趣,值得 一些额外的代码,以明确它们如何被释放。

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

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

答案 2 :(得分:6)

  

但如果你不得不担心   手动处理东西,然后是什么   GC的要点?!那不是GC   为编码员照顾这个?

问题是你不知道何时 GC会运行。如果你的应用程序从不压力记忆,它可能根本不运行。

答案 3 :(得分: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到达它时已经消失了

More on IDisposable

using is your friend - 这是我们到目前为止最接近RAII

答案 4 :(得分:4)

实现IDisposable的对象试图告诉您它们与非托管内存的结构有链接。垃圾收集器分批运行以提高效率。但这意味着在您的对象被丢弃之前可能需要一段时间,这意味着您将保留比您应该更长的资源,这可能会对性能/可靠性/可伸缩性产生负面影响。

答案 5 :(得分:3)

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

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

当它们超出范围时,会自动调用连接和命令对象上的dispose,这是执行离开块的时候。

答案 6 :(得分: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块中。

答案 7 :(得分:2)

  

...... GC从未打算管理资源;它旨在管理内存分配......   在数据库连接的特定情况下,您正在处理除内存之外的其他资源......(Scott Dorman)

OP没有使用特定平台进行标记,但大多数答案都是.net特定的,注意到GC主要是为了避免内存泄漏,而是扩展,例如using表达式和{{1可以帮到很多。

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

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

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

答案 8 :(得分:1)

上面提到的代码释放了获得的资源(虽然,我不相信你应该自己调用Dispose()方法,通过释放资源意味着关闭流和类似的东西)。 GC从内存中删除对象(释放对象使用的内存),但只有在对象释放资源后才能完成。

答案 9 :(得分:1)

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

答案 10 :(得分:1)

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

在数据库连接的特定情况下,您正在处理除内存之外的其他资源 - 特别是连接池,可能的隐式事务范围等。通过调用Close()和/或Dispose(),您明确告诉对象在受管资源等待GC周期发生时立即释放这些非托管资源。

答案 11 :(得分:1)

我现在看到啊。我将内存管理与非托管资源管理混为一谈。 谢谢你的澄清!

答案 12 :(得分:0)

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

答案 13 :(得分:0)

在释放数据库连接或文件句柄等外部资源时,GC受到限制。但是,为了在.NET世界中分配内存,它会处理许多平凡的内存管理任务。

答案 14 :(得分:0)

上面提供了一个示例,说明当本地分配的内存或资源作为句柄漫游到托管世界时。在这种情况下,因为托管世界没有分配内存,所以它无法“自动整理”它。必须明确地处置存储器/资源,或者至少将其置于终结器内。

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

相关问题