区分实现IDisposable 的对象和带有终结器的对象很重要。在大多数情况下(可能最好全部),带有终结器的对象也实现了IDisposable,但它们实际上是两个不同的东西,最常一起使用。
终结器是一种对 .NET 运行时说,在它收集对象之前,它必须执行终结器的机制。当 .NET 运行时检测到对象符合垃圾回收条件时,就会发生这种情况。通常,如果对象没有终结器,它将在此收集期间被收集。如果它有一个终结器,它将被放置到一个列表中,即“freachable queue”,并且有一个后台线程监控这个线程。有时在集合将对象放入此队列后,终结器线程会处理此队列中的对象并调用终结器方法。
一旦发生这种情况,该对象再次符合回收条件,但它也被标记为已完成,这意味着当垃圾回收器在未来的回收周期中找到该对象时,它不再将其放入此队列,而是正常收集。
请注意,在上面的文字段落中,IDisposable 没有被提及一次,这是有充分理由的。以上都不依赖IDisposable根本。
现在,实现IDisposable 的对象可能有也可能没有终结器。一般规则是,如果对象本身拥有非托管资源,它可能应该拥有,如果不拥有,则可能不应该拥有。 (我很犹豫要不要说永远和永远不会在这里,因为似乎总是有人能够找到一个角落案例,它以某种方式有意义但违反了“典型”规则) p>
TL;DR 对上述内容的总结可能是,终结器是一种在收集对象时获得(半)保证清理对象的方法,但确切的时间不是直接在程序员的控制之下,而实现IDisposable 是一种直接从代码中控制这种清理的方法。
无论如何,让我们来解决您的具体问题:
当 C# .NET 4.5(或更高版本)应用程序关闭时,未正确处理的 IDisposable 会发生什么情况?
回答:什么都没有。如果它们有终结器,终结器线程将尝试拾取它们,因为当程序终止时,所有对象都可以被收集。但是,终结器线程不允许“永远”运行来执行此操作,因此它也可能会超时。另一方面,如果实现 IDisposable 的对象没有终结器,它只会被正常收集(同样,IDisposable 与垃圾收集完全无关)。
但是假设我有一个带有静态 Stream 变量的控制台应用程序。当我关闭控制台应用程序时它会被释放吗?
回答:不,不会处理。 Stream 本身是一个基类,因此取决于具体的派生类,它可能有也可能没有终结器。但是,它遵循与上述相同的规则,因此如果它没有终结器,它将被简单地收集。例如,MemoryStream 没有终结器,而FileStream 有。
HttpClient 呢?你怎么知道它在哪些情况下可以,在哪些情况下不可以
答案:reference source for HttpClient 似乎表明HttpClient 没有终结器。因此它会被简单地收集起来。
好的,现在是一些实际的背景信息。我经常将某些 IDisposable 存储为字段,迫使我的班级实现 IDisposable。最终用户应该使用 using。但如果这没有发生呢?
答案:如果您忘记/不调用实现IDisposable 的对象上的IDisposable.Dispose(),那么一旦对象符合收集条件,我在此处所述的有关终结器的所有内容仍然会发生。除此之外,不会发生什么特别的事情。对象是否实现IDisposable 与垃圾收集过程无关,只有终结器的存在。
在 GC 之前它只是不必要的内存吗?还是你突然出现内存泄漏
答案:根据这个简单的信息无法确定。这取决于Dispose 方法会做什么。例如,如果对象已经在某处注册了自己,因此在某处存在对它的引用,那么某些代码停止使用该对象可能实际上不会使该对象符合收集条件。 Dispose 方法可能负责注销它,删除对它的最后引用。所以这取决于对象。仅仅对象实现IDisposable 的事实不会造成内存泄漏。如果删除了对该对象的最后一个引用,则该对象有资格被收集,并将在未来的收集周期中被收集。
备注: