C# - 超出范围时是否会立即销毁对象?

时间:2009-09-26 09:45:23

标签: c# garbage-collection raii

我是否可以相信一个对象被销毁,并且当它超出C#中的范围时会立即调用它的析构函数?

我认为应该这样做,因为许多常见的编码实践(例如事务对象)依赖于这种行为,但我并不习惯使用垃圾收集,并且对这些语言通常的行为方式几乎没有了解。

感谢。

6 个答案:

答案 0 :(得分:27)

不,。Net,因此C#依赖于垃圾收集内存管理。所以析构函数(在.Net中称为终结函数)在GC发现销毁对象之前不会被调用。

此外:C#中的大多数“常规”对象没有析构函数。如果您需要析构函数模式,则应使用IDisposable interface实现Dispose Pattern。在一次性对象上,您还应该确保调用Dispose方法,使用using keyword或直接调用方法。

进一步(希望)澄清:确定性处置在.Net中是有用的,例如:当您需要显式释放不由.Net运行时管理的资源时。此类资源的示例是文件句柄,数据库连接等。通常重要的是,一旦不再需要这些资源,就将其释放。因此,我们无法等待GC释放它们。

为了在.Net GC的非确定性世界中获得确定性处理(类似于C ++的范围行为),.Net类依赖于IDisposable接口。借用Dispose Pattern,这里有一些例子:

首先,实例化一次性资源,然后让对象超出范围,将其留给GC处理对象:

1.    {
2.       var dr = new DisposableResource();
3.    }

要解决这个问题,我们可以明确地处理对象:

1.    {
2.       var dr = new DisposableResource();
3.
4.       ...
5.
6.       dr.Dispose();
7.    }

但如果第2行和第6行之间出现问题怎么办?不会调用Dispose。为了进一步确保最终调用Dispose而不管任何例外,我们可以执行以下操作:

1.    var dr = new DisposableResource();
2.    try
3.    {
4.       ...
5.    }
6.    finally
7.    {
8.       dr.Dispose();
9.    }

由于经常需要这种模式,C#包含using关键字以简化操作。以下示例等同于上述示例:

1.    using (var dr = new DisposableResource())
2.    {
3.       ...
4.    }

答案 1 :(得分:10)

没有。 对象实际上并不“超出范围”,对它的引用(即用于访问它的变量)确实如此。

一旦没有对给定对象的更多引用,如果需要,该对象将成为符合条件的垃圾收集(GC)。每当GC决定它需要回收你的不再引用的对象的空间时,就会调用对象终结器。

如果您的对象是资源(例如文件句柄,数据库连接),它应该实现IDisposable接口(这使得对象有义务实现Dispose()方法来清理任何打开的连接等)。在这种情况下,您的最佳做法是将对象创建为using块的一部分,以便在此块完成时,您的应用程序将自动调用对象Dispose()方法,这将占用关闭你的文件/数据库连接/等等。

e.g。


using (var conn = new DbConnection())  
{ 
   // do stuff with conn  
} // conn.Dispose() is automatically called here.  

using块只是一些语法糖,它包含您与conn块中try对象的交互,以及仅调用{finally块的conn.Dispose()块1}}

答案 2 :(得分:4)

在C#中没有像C ++那样的析构函数。 (C#中有一个不同的析构函数概念,也称为终结函数,它使用与C ++析构函数相同的语法,但它们与销毁对象无关。它们旨在为非托管资源提供清理机制。) 垃圾收集器将在不再引用之后的某个时间清理对象。没有立即,也没有办法保证这一点。

幸运的是,你也不想保证这一点。如果你需要内存,那么GC会回收它。如果你不这样做,为什么要关心周围是否还有一些垃圾对象?这不是内存泄漏:GC仍然可以找到它并随时清理它。

答案 3 :(得分:4)

不,这不能保证。与Java等语言类似,在C#中,垃圾收集器在需要时运行(即堆积过满时)。但是,当您的对象实现IDisposable时,i。即他们使用Dispose()方法并且必须调用它,然后您可以利用using关键字:

using (var foo = new DisposableObject()) {
    // do something with that
}

当离开Dispose()块时,将立即调用using

注意:IDisposable有很多类型,最常见的是GDI +,还有数据库连接,事务等等。所以它可能真的是正确的模式。

注意2:在幕后的幕后将转换为try / finally块:

var foo = new DisposableObject();
try
{
    // do something with that
}
finally
{
    foo.Dispose();
}

但是这个翻译是由编译器完成的,并且非常方便忘记拨打Dispose()

答案 4 :(得分:0)

我认为你不应该以这种方式依赖垃圾收集者。即使你扣除它们的运行方式,也很可能在下一个版本中它们已经重新实现了它。

在任何情况下,当您取消引用它们时,对象不会被垃圾收集。通常会收集它们,直到达到某个阈值,然后释放它们。

特别是在java程序中,当您查看任务管理器上的内存消耗时,这一点非常明显。它会逐渐增长,并且每隔一分钟就会突然下降。

答案 5 :(得分:0)

没有。如果您参考CLI规范(关于终结器的第8.9.6.7节)http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf,您可以找到以下内容

  

CLI 确保在实例成功后立即调用终结器   无法访问。在依靠记忆压力的同时   触发终结是可以接受的,实施者应该考虑使用额外的   度量

但一定不能。