我完全知道使用语句是处理IDisposable的方法。请不要在评论中重复此建议。
当C#.NET 4.5(或更高版本)应用程序关闭时,未正确处置的IDisposables会发生什么?
我知道有些可以终结器来处理非托管资源。
但是,假设我有一个控制台应用程序,带有静态Stream变量。当我关闭控制台应用程序时会丢弃它吗?
HttpClient呢?以及您如何知道在哪些情况下会发生这种情况?
好的,现在提供一些实际的背景信息。我经常将某些IDisposables存储为字段,从而迫使我的类实现IDisposable。最终用户应使用using。但是,如果那没有发生怎么办?
在GC之前是否只是不必要的内存?还是突然有内存泄漏?
答案 0 :(得分:9)
重要的是要区分实现IDisposable
的对象和带有终结器的对象。在大多数情况下(可能最好是所有情况),带有终结器的对象也实现IDisposable
,但实际上它们是两个截然不同的事物,最常一起使用。
终结器是一种对.NET运行时说的机制,在它可以收集对象之前,它必须执行终结器。当.NET运行时检测到对象符合垃圾收集条件时,就会发生这种情况。通常,如果对象没有终结器,则将在此收集期间将其收集。如果它具有终结器,则将其放置在列表中,即“可访问队列”,并且有一个监视该线程的后台线程。有时,在集合将对象放置到此队列之后,终结器线程将从该队列中处理该对象并调用finalizer方法。
一旦发生这种情况,该对象将再次有资格进行收集,但是它也被标记为已完成,这意味着当垃圾收集器在以后的收集周期中找到该对象时,它不再将其放置在此队列中,而是正常收集。
请注意,在以上文本段落中,没有IDisposable
被提及,这是有充分理由的。以上所有都不依赖于IDisposable
。
现在,实现IDisposable
的对象可能具有终结器,也可能没有终结器。一般规则是,如果对象本身拥有非托管资源,则它可能应该拥有,如果没有,则不应该拥有。 (我很犹豫,总是在这里永远不会说,因为似乎总有人能找到一个可以以某种方式有意义但又打破“典型”规则的拐角处) >
上面的 TL; DR 总结可能是终结器是一种在收集对象时获得(半)保证的对象清除的方法,但是确切的说,发生时间不是直接在程序员的控制之下,而实现IDisposable
是一种直接从代码控制清理的方法。
无论如何,我们将带您解决所有具体问题:
当C#.NET 4.5(或更高版本)应用程序关闭时,未正确处置的IDisposables会发生什么?
答案:没有。如果它们具有终结器,则终结器线程将尝试拾取它们,因为当程序终止时, all 对象将有资格进行收集。但是,不允许终结器线程“永远”运行以执行此操作,因此它也可能用完时间。另一方面,如果实现IDisposable
的对象没有终结器,则将正常地对其进行收集(同样,IDisposable
与垃圾回收完全没有关系)。
但是,假设我有一个控制台应用程序,带有静态Stream变量。当我关闭控制台应用程序时会丢弃它吗?
答案:否,不会处置。 Stream
本身是基类,因此取决于具体的派生类,它可能有也可能没有终结器。但是,它遵循与上述相同的规则,因此,如果没有终结器,则将仅对其进行收集。例如,MemoryStream没有终结器,而FileStream有终结器。
HttpClient呢?以及您如何知道在哪些情况下会发生这种情况,
答案:reference source for HttpClient似乎表明HttpClient
没有终结器。因此,它将被简单地收集。
好的,现在提供一些实际的背景信息。我经常将某些IDisposables存储为字段,从而迫使我的类实现IDisposable。最终用户应使用using。但是,如果那没有发生怎么办?
答案::如果您忘记/不要在实现IDisposable.Dispose()
的对象上调用IDisposable
,则一旦对象完成,我在这里所说的有关终结器的一切都会发生。符合收集条件。除此之外,没有什么特别的事情发生。无论对象实现IDisposable
是否与垃圾回收过程无关,只有终结器的存在。
在GC之前是否只是不必要的内存?还是突然有内存泄漏
答案:根据此简单信息无法确定。这取决于Dispose
方法的作用。例如,如果对象已在某处注册了自己,以便在某处对其进行引用,则某些代码停止使用该对象可能实际上未使该对象有资格进行收集。 Dispose
方法可能负责注销它,并删除对该方法的最后一个引用。因此,这取决于对象。对象仅实现IDisposable
的事实并不会造成内存泄漏。如果删除了对该对象的最后一个引用,则该对象将符合收集条件,并将在以后的收集周期中收集。
备注:
请注意,上面的文字也可能得到了简化。由于没有意义,在应用程序终止时可能没有完成一个完整的收集周期以实际“收集内存”。无论如何,操作系统将释放进程分配的内存。可能仅执行终结处理。 (意思是,我不知道一种或另一种方式在这里完成了哪种优化)
这里最重要的部分是您需要区分 程序执行期间和 程序执行之后
答案 1 :(得分:2)
该过程即将消失。这意味着操作系统将停止提供任何内存来支持该进程的地址空间 1 。无论如何,所有内存都被“回收”。它还将终止进程中的所有线程。因此不会进行进一步处理。无论如何,所有手柄都将关闭。
您需要担心的事情是外部现实。例如。如果您有未处置的Stream
,如果它通过网络或文件连接到某个东西,它是否会清空需要的所有内容?
是的,如果您自己存储Disposable
,则应实现IDisposable
。但是,如果您没有任何 unmanaged 资源,请不要实现终结器。在终结器运行时,您不应从该代码访问任何其他托管对象,因此处理它们为时已晚。
如果有人忘记处理您的对象,这就是他们的错误,并且您通过广告宣传应该通过IDisposable
界面进行处理,从而完成了应做的事情。
1 进程没有内存。它们具有由OS管理的地址空间。有时可能需要将该地址空间的某些部分存储在内存中。这是操作系统在虚拟内存时代的工作,并且只有每个真正的“临时”将物理内存临时“借给”任何给定的进程。它可以随时带走
答案 2 :(得分:2)
什么都不会自动处置。
实现IDisposable
接口的类之所以这样设计,是因为它们要么使用IDisposable
字段(例如您的情况),要么使用非托管资源(该规则有例外,但这是不可行的)此答案的范围)。
CLR的任何部分都不会调用Dispose
方法。
GC将收集引用,除非另有指示(通过使用GC.SuppressFinalize()
,然后将引用移至终结器队列,在此处
它将通过调用它的finalize方法来完成。
当且仅当该类已显式重写finalize方法并在finalize方法中调用Dispose
时,该实例才最终被处置。
因此,如果要确保终结器可以处理您的类,则必须override the finalize method in your class。但是,请注意-Implementing the Finalize method correctly is hard!
话虽这么说,当您实现IDisposable
接口时,您是在告诉使用此类的任何人应该处置它。他们是否真正处置它不再是您的责任-这是他们的责任。因此,如果确实存在内存泄漏(很有可能会发生一次)-假设您的班级为implemented the IDisposable
interface correctly,那不是您的问题。
答案 3 :(得分:1)
C#应用程序退出会自动处理托管资源吗?
是的,是的,不是,但是从技术上来说:不是。
如果IDisposable
接口正确实现,则在收集对象时将调用dispose函数。如果未正确实施,则使用非托管资源将导致泄漏。
当C#.NET 4.5(或更高版本)应用程序关闭时,未正确处置的IDisposables会发生什么?
释放了处理空间。如果IDisposable
隐式地在其他进程空间中保留了内存,并且没有正确处理:您有泄漏。
但是,假设我有一个控制台应用程序,带有静态Stream变量。当我关闭控制台应用程序时会丢弃它吗?
尽管隐式,但它可能导致意外结果,例如在您终止进程时。例如:TCP端口可能处于等待状态。
HttpClient呢?以及您如何知道在哪些情况下会发生这种情况?
与上述相同。
好的,现在提供一些实际的背景信息。我经常将某些IDisposables存储为字段,从而迫使我的类实现IDisposable。最终用户应使用using。但是,如果那没有发生怎么办?
您应该正确实施它,但建议进行处置。例如文件句柄。如果未正确处理该文件,则对该文件的另一个调用可能会失败,因为该文件仍处于打开状态。这可能会在GC中解决,但您不知道何时。
在GC之前是否只是不必要的内存?还是突然有内存泄漏?
更多,泄漏,打开的端口,打开的手柄,视频内存消耗等。
有关IDisposable
的正确实现,请参见:Proper use of the IDisposable interface
如果您阅读它,可以想象并非所有第三方库都在正确实现它。