当我致电object.Dispose()
时; CLR会立即从内存中销毁对象,还是在下一个循环中标记要删除的对象?
我们在Dispose()之后立即调用GC.SuppressFinalize()
,这是否意味着,“不要再次收集对象以进行处置,因为它已经提交给了移位”。
实际上哪一代负责破坏,我猜第二代。
答案 0 :(得分:11)
首先,IDisposable.Dispose
和GC不是一回事。
GC将清理内存使用情况,IDisposable.Dispose
用于确定性地释放资源,如文件句柄,数据库连接,网络连接等。
让我们先解决最终问题。
如果一个对象声明了一个终结器,那么当GC出现以从内存中释放它时,该对象将被特别处理。该对象不是一次被释放,而是单独列出。
在后台,终结线程正在运行此列表并调用此列表中对象的终结器方法。调用终结器方法后,将从列表中删除该对象。
这里的要点是,当对象在此列表中时,它不符合收集条件。这意味着具有终结器的对象一旦有资格进行收集,将暂时转换为不再符合收集条件但尚未完成的状态。再次发现对象后,在终结器运行并从该列表中删除后,它将从内存中释放。
GC.SuppressFinalize
只是一个对象说“终结器不再需要运行的方法,如果你发现这个对象有资格收集,只需立即释放它”。
IDisposable.Dispose
一旦被一个对象实现,就与垃圾收集器并不完全相关。 GC系统中没有内置任何内容可确保调用Dispose
方法。这意味着可以轻松释放具有Dispose
方法的对象,而不会调用Dispose
。
此外,调用Dispose
不会以任何方式使对象符合收集条件。使对象符合收集条件的唯一方法是删除对它的所有强引用,通常是让局部变量超出范围(方法返回),或者从其他对象(如列表,事件处理程序,等)
“链接”,如果要调用它,那么内部具有此类资源的对象通常会实现终结器(如果资源未受管理)。如果资源是托管资源,如FileStream
对象,则该对象将根据需要自行完成最终确定。但是,如果您持有非托管资源,例如通过P / Invoke检索的文件句柄,那么您应该实现终结器。
通常情况下,该对象的终结器和IDisposable.Dispose
都会清理该资源,然后典型的Dispose
呼叫GC.SuppressFinalize
说“我已经处理好了它,你可以释放该对象,如果它有资格收集。“
但是,如果您只是调用Dispose
,但仍保留对该对象的引用(事件处理程序,静态字段,局部变量等),则该对象尚不符合收集条件且不会被释放
所以,总结一下:
Dispose
通常是为了释放资源(非托管或托管)。 不以任何方式影响GC是否可以收集对象,或何时完成。加分问题:
如果符合以下条件,您认为会发生什么:
希望这回答了你的问题,如果没有,请发表评论或澄清你的问题,我会编辑我的答案。
答案 1 :(得分:4)
Dispose
是一种正常的CLR方法,通常会调用GC.SuppressFinalize
。垃圾收集器与它无关,调用Dispose
对GC没有特殊意义。
如果Dispose
调用GC.SuppressFinalize(this)
,则GC在收集对象时不会运行终结器。
但是,这并不意味着将更快地收集对象。
答案 2 :(得分:3)
不,Dispose
不会导致垃圾收集器收集对象。您在许多GC.SuppressFinalize()
方法中看到Dispose
的原因是因为许多一次性类实现了终结器以确保处置。我会解释一下。
如果我实现了一个包含关键资源的类,并且我想确定(好吧,几乎可以肯定)我的类被正确处理掉了,那么我可能不仅仅依赖于我的班级要拨打Dispose
或使用using
。相反,我可以实现一个终结器,在大多数情况下,当GC收集我的对象时会调用它。在终结器中,我会强制拨打Dispose
。
但是带有终结器的对象对于垃圾收集器来说更复杂。它会更长时间地停留,占用更多内存,并在收集时花费更多时间进行处理。
因此,如果用户 记得正确处置,那么我们可以告诉GC它不需要为此对象调用终结器。
答案 3 :(得分:1)
对象的生成是指它“幸存”了多少垃圾收集(2是最高的)。当一个对象被创建时,它在第0代,并且只有当对象在收集时可以访问或“实时”(具有强引用)时才会被提议给下一代。
这不包括具有复杂终结器的第0代对象,因为终结器在单独的线程上运行,一旦完成,就以通常的方式收集。他们没有升职。
第2代对象的集合可能需要很长时间,因为GC可能只需要收集第0代对象以释放堆上足够的空间。要强制完整集合,请调用GC.Collect()。