如果我理解正确,.net运行时将始终在我之后清理。因此,如果我创建新对象并停止在我的代码中引用它们,运行时将清理这些对象并释放它们占用的内存。
既然这就是为什么一些对象需要有一个析构函数或dispose方法呢?当它们不再被引用时,运行时不会清理它们吗?
答案 0 :(得分:93)
需要终结器来保证将稀缺资源释放回系统,如文件句柄,套接字,内核对象等。由于终结器总是在对象生命周期结束时运行,因此它是释放这些句柄的指定位置。
Dispose
模式用于提供确定性的资源破坏。由于.net运行时垃圾收集器是非确定性的(这意味着您永远无法确定运行时何时将收集旧对象并调用其终结器),因此需要一种方法来确保系统资源的确定性释放。因此,当您正确实现Dispose
模式时,您可以提供资源的确定性释放,并且在消费者粗心并且不处理对象的情况下,终结器将清理对象。
为什么需要Dispose
的一个简单示例可能是一个快速而又脏的日志方法:
public void Log(string line)
{
var sw = new StreamWriter(File.Open(
"LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None));
sw.WriteLine(line);
// Since we don't close the stream the FileStream finalizer will do that for
// us but we don't know when that will be and until then the file is locked.
}
在上面的示例中,文件将保持锁定状态,直到垃圾收集器调用StreamWriter
对象上的终结器。这提出了一个问题,因为在此期间,可能会再次调用该方法来编写日志,但这次它会因为文件仍然被锁定而失败。
正确的方法是在完成后使用它来处置对象:
public void Log(string line)
{
using (var sw = new StreamWriter(File.Open(
"LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))) {
sw.WriteLine(line);
}
// Since we use the using block (which conveniently calls Dispose() for us)
// the file well be closed at this point.
}
顺便说一下,技术上的终结者和破坏者意味着同样的事情;我更喜欢称c#destructors'终结器',否则它们往往会让人混淆C ++析构函数,这与C#不同,是确定性的。
答案 1 :(得分:20)
之前的答案很好,但我再次强调这一点。特别是,你说过
如果我理解正确,.net运行时将始终在我之后清理。
这只是部分正确。实际上, .NET only 为一个特定资源:主内存提供自动管理。所有其他资源都需要手动清理。 1)
奇怪的是,主存在几乎所有关于程序资源的讨论中都处于特殊状态。当然有一个很好的理由 - 主存储器通常是最稀缺的资源。但值得记住的还有其他类型的资源,也需要管理。
1)通常尝试的解决方案是将其他资源的生命周期与代码中的内存位置或标识符的生命周期相结合 - 因此存在终结器。
答案 2 :(得分:9)
只有在系统没有内存压力的情况下才会运行垃圾收集器,除非它确实需要释放一些内存。这意味着,您永远无法确定GC何时运行。
现在,想象一下你是一个数据库连接。如果你在GC之后清理GC,你可能会连接到数据库的时间比需要的时间长得多,导致奇怪的负载情况。在这种情况下,您希望实现IDisposable,以便用户可以调用Dispose()或使用using()来确保尽快关闭连接,而不必依赖可能在以后运行的GC。
通常,IDisposable是在任何使用非托管资源的类上实现的。
答案 3 :(得分:4)
答案 4 :(得分:2)
真正的原因是因为.net垃圾收集不是为了收集非托管资源而设计的,因此这些资源的清理仍然掌握在开发人员手中。 此外,当对象超出范围时,不会自动调用对象终结器。它们在某个未确定的时间由GC调用。当他们被调用时,GC不会立即运行它,等待下一轮调用它,增加清理时间,当对象持有稀缺的非托管资源(例如文件)时,这不是一件好事或网络连接)。 输入一次性模式,开发人员可以在确定的时间(调用yourobject.Dispose()或using(...)语句时)手动释放稀缺资源。 请记住,你应该调用GC.SuppressFinalize(this);在您的dispose方法中告诉GC该对象是手动处理的,不应该最终确定。 我建议你看看K. Cwalina和B. Abrams的框架设计指南书。它解释了Disposable模式非常好。
祝你好运!
答案 5 :(得分:2)
简单的解释:
实施Finalize方法的一些准则:
实施Dispose方法的一些准则:
答案 6 :(得分:1)
需要descructors和dispose方法的对象正在使用未受管理的资源。因此垃圾收集器无法清理这些资源,您必须自己完成。
查看IDisposable的MSDN文档; http://msdn.microsoft.com/en-us/library/system.idisposable.aspx
该示例使用非托管处理程序 - IntPr。
答案 7 :(得分:0)
某些对象可能需要清理低级别的项目。比如需要关闭的硬件等等。
答案 8 :(得分:0)
主要用于非托管代码,以及与非托管代码的交互。 “纯”托管代码永远不需要终结器。另一方面,一次性使用只是一种方便的模式,可以在你完成任务后强行释放。
答案 9 :(得分:0)
.NET垃圾收集器知道如何在.NET运行时中处理托管对象。但Dispose模式(IDisposable)主要用于应用程序正在使用的非托管对象。
换句话说,.NET运行时不一定知道如何处理每种类型的设备或处理那些(关闭网络连接,文件句柄,图形设备等),所以使用IDisposable提供了一种说法“让我在一个类型中实现一些自己的清理”。看到这个实现,垃圾收集器可以调用Dispose()并确保清理托管堆之外的东西。
答案 10 :(得分:0)
有一些(很少)的情况下,当不再使用纯粹的托管对象时,可能需要执行特定的操作,我无法想出一个顶端的例子,但我有多年来看到了几种合法用途。但主要原因是清理对象可能正在使用的任何非托管资源。
因此,通常,除非使用非托管资源,否则不需要使用Dispose / Finalize模式。
答案 11 :(得分:0)
因为垃圾收集器无法收集托管环境未分配的内容。因此,需要以旧方式收集对导致内存分配的非托管API的任何调用。