垃圾收集器将调用IDisposable.Dispose为我?

.NET的IDisposable模式 意味着 ,如果你写一个终结器,并实现IDisposable,你的终结器需要显式调用Dispose。 这是合乎逻辑的,也是我在罕见情况下一直在做的一件事情。

但是,如果我只是这样做会发生什么:

class Foo : IDisposable { public void Dispose(){ CloseSomeHandle(); } } 

不要实现一个终结器,或者任何东西。 框架会为我调用Dispose方法吗?

是的,我意识到这听起来很愚蠢,所有的逻辑暗示它不会,但是我总是在头脑后面有两件事让我不确定。

  1. 有些人几年前曾经告诉过我,实际上是这样做的,那个人有一个“知道他们的东西”的良好logging。

  2. 编译器/框架根据你实现的接口(例如:foreach,扩展方法,基于属性的序列化等等)来执行其他“魔术”的事情,所以这也许是“神奇”的。

虽然我已经阅读了很多关于它的东西,但是有很多暗示的东西,我从来没有能够find这个问题的肯定答案。

.net垃圾收集器调用垃圾回收对象的Object.Finalize方法。 默认情况下,这个function什么都不做 ,如果你想释放额外的资源,那么这个function就不能使用。

Dispose不会自动调用,如果要释放资源,例如在“using”或“try finally”块中,必须显式调用

请参阅http://msdn.microsoft.com/en-us/library/system.object.finalize.aspx以获取更多信息;

我想在他的评论中强调布赖恩的观点,因为这很重要。

Finalizer不是像C ++中那样的确定性析构函数。 正如其他人所指出的那样,不能保证什么时候会被叫到,而且如果你有足够的记忆,是否会被叫到

但是终结者的坏消息是,就像Brian所说的那样,它会导致你的对象在垃圾回收中幸存下来。 这可能是坏的。 为什么?

你可能会也可能不知道,GC被分成几代 – Gen 0,1和2,再加上Large Object Heap。 拆分是一个松散的术语 – 你得到一块内存,但有指针​​的第0代对象开始和结束。

思考的过程是,你可能会使用很多短暂的对象。 所以这些应该是容易和快速的GC到达第0代的对象。 所以当有内存压力的时候,它所做的第一件事就是Gen 0的收集。

现在,如果解决不了足够的压力,那么它将返回并执行Gen 1扫描(重新生成Gen 0),如果还不够,则执行Gen 2扫描(重新生成Gen 1和Gen 0)。 因此,清理长寿命的对象可能需要一段时间,而且相当昂贵(因为您的线程可能会在操作过程中暂停)。

这意味着如果你做这样的事情:

 ~MyClass() { } 

你的对象,无论如何,都会活到第二代。这是因为在垃圾回收期间,GC没有办法调用终结器。 因此,必须完成的对象被移动到一个特殊的队列中,通过不同的线程清除(finalizer线程 – 如果你杀了,会使各种不好的事情发生)。 这意味着你的对象更长时间的停留,并且可能迫使更多的垃圾收集。

所以,所有这一切只是为了让您想要使用IDisposable来尽可能清理资源,并认真地尝试find使用终结器的方法。 这是您应用程序的最佳利益。

这里已经有很多很好的讨论了,我晚了一点,但我想自己补充几点。

  • 垃圾收集器不会直接为您执行Dispose方法。
  • 当GC感觉到时,GC 执行终结器。
  • 一个用于具有终结器的对象的常见模式是让它调用一个按照约定定义为Dispose(bool disposing)传递false的方法,以表明调用是由finalization而不是显式的Dispose调用产生的。
  • 这是因为在完成一个对象(它们可能已经完成)的同时,对其他被pipe理对象作出任何假设是不安全的。
 class SomeObject : IDisposable { IntPtr _SomeNativeHandle; FileStream _SomeFileStream; // Something useful here ~ SomeObject() { Dispose(false); } public void Dispose() { Dispose(true); } protected virtual void Dispose(bool disposing) { if(disposing) { GC.SuppressFinalize(this); //Because the object was explicitly disposed, there will be no need to //run the finalizer. Suppressing it reduces pressure on the GC //The managed reference to an IDisposable is disposed only if the _SomeFileStream.Dispose(); } //Regardless, clean up the native handle ourselves. Because it is simple a member // of the current instance, the GC can't have done anything to it, // and this is the onlyplace to safely clean up if(IntPtr.Zero != _SomeNativeHandle) { NativeMethods.CloseHandle(_SomeNativeHandle); _SomeNativeHandle = IntPtr.Zero; } } } 

这是简单的版本,但有很多细微差别,可以让你在这种模式。

  • IDisposable.Dispose的合同表明多次调用必须是安全的(对已经处理的对象调用Dispose将不会执行任何操作)
  • 正确pipe理一次性对象的inheritance层次结构会变得非常复杂,特别是当不同的层引入了新的Disposable和非托pipe资源时。 在上面的模式Dispose(布尔)是虚拟的,以便它可以被重写,以便它可以被pipe理,但我觉得它是错误倾向。

在我看来,完全避免任何直接包含一次性引用和可能需要最终确定的本地资源的types会好得多。 SafeHandles提供了一个非常干净的方式,通过将本地资源封装到一次性内部提供它们自己的最终化(以及一些其他好处,例如在P / Invoke期间由于asynchronousexception而可能丢失本地句柄) 。

简单地定义一个SafeHandle使这个琐碎:

 private class SomeSafeHandle : SafeHandleZeroOrMinusOneIsInvalid { public SomeSafeHandle() : base(true) { } protected override bool ReleaseHandle() { return NativeMethods.CloseHandle(handle); } } 

允许你简化包含types到:

 class SomeObject : IDisposable { SomeSafeHandle _SomeSafeHandle; FileStream _SomeFileStream; // Something useful here public virtual void Dispose() { _SomeSafeHandle.Dispose(); _SomeFileStream.Dispose(); } } 

我不这么认为。 您可以控制何时调用Dispose,这意味着您可以在理论上编写处理代码,以便对(例如)其他对象的存在进行假设。 您无法控制终结器被调用的时间,因此可能有终止器自动以您的名义调用Dispose。


编辑:我走了,testing,只是为了确保:

 class Program { static void Main(string[] args) { Fred f = new Fred(); f = null; GC.Collect(); GC.WaitForPendingFinalizers(); Console.WriteLine("Fred's gone, and he's not coming back..."); Console.ReadLine(); } } class Fred : IDisposable { ~Fred() { Console.WriteLine("Being finalized"); } void IDisposable.Dispose() { Console.WriteLine("Being Disposed"); } } 

不是在你描述的情况下,但如果你有的话,GC会给你打电话给终结者

然而。 接下来的垃圾收集,而不是被收集,对象将进入最终确定,所有的东西都被收集起来,然后被终结器调用。 之后的收集将被释放。

根据你的应用程序的内存压力,你可能暂时没有用于该对象生成的gc。 因此,在文件stream或数据库连接的情况下,您可能需要等待一段时间,非托pipe资源才能在终结器调用中释放一段时间,从而导致一些问题。

GC 不会调用处置。 它可能会打电话给你的终结者,但即使这样也不能保证在任何情况下。

看到这篇文章的最佳处理方法的讨论。

不,不叫。

但是这很容易不要忘记处理你的对象。 只要使用关键字。

我做了以下testing:

 class Program { static void Main(string[] args) { Foo foo = new Foo(); foo = null; Console.WriteLine("foo is null"); GC.Collect(); Console.WriteLine("GC Called"); Console.ReadLine(); } } class Foo : IDisposable { public void Dispose() { Console.WriteLine("Disposed!"); } 

关于IDisposable的文档给出了一个非常清晰和详细的行为解释,以及示例代码。 GC不会调用接口上的Dispose()方法,但会调用您的对象的终结器。

IDisposable模式主要由开发人员调用,如果您有一个实现IDispose的对象,开发人员应该在对象的上下文周围实现using关键字或直接调用Dispose方法。

模式的失败安全是实现调用Dispose()方法的终结器。 如果你不这样做,你可能会创build一些内存泄漏,即:如果你创build了一些COM包装,并且从来没有调用System.Runtime.Interop.Marshall.ReleaseComObject(comObject)(它将被放置在Dispose方法中)。

除了跟踪包含终结器的对象并将它们存储在GC的Finalizer表中并在GC启动一些清理启发式时调用它们之外,clr中没有任何魔术可以自动调用Dispose方法。