调用 GC.SuppressFinalize 的对象会发生什么?我知道这意味着GC不会调用这些对象的终结器(析构函数),如果是这样,这些对象什么时候会真正被破坏?否则会有内存泄漏对吗?
您对最终确定的用途有误解。终结是为了清理非托管内存的资源。
假设您有一个包含整数字段的引用类型对象。该整数字段恰好是通过调用非托管代码打开文件而获得的文件的句柄。
由于某些其他程序可能想要访问该文件,因此尽快关闭该文件是有礼貌的。但是.NET 运行时不知道这个整数对操作系统有什么特殊意义。它只是一个整数。
解决此问题的方法通常是将对象标记为实现 IDisposable,然后在完成后立即在对象上调用“Dispose”。您对“Dispose”的实现然后会关闭该文件。
请注意,这里没有什么特别的。清理非托管资源的方法称为“Dispose”,而需要释放的对象实现 IDisposable,这只是一个约定。垃圾收集对此一无所知。
那么现在问题来了:如果有人忘记调用 Dispose 怎么办?文件是否永远保持打开状态?(明明进程结束时文件会被关闭,但是如果进程运行了很长时间呢?)
要解决此问题,您可以使用终结器。这是如何运作的?
当一个对象即将被垃圾回收时,垃圾回收器会检查它是否有终结器。如果是这样,那么它不是垃圾收集它,而是将它放在终结器队列中。在未来某个未指定的时间点,一个线程运行检查队列并在每个对象上调用一个特殊的“Finalize”方法。之后,该对象从终结队列中移除并标记为“嘿,我已经终结了”。该对象现在再次符合收集条件,因此垃圾收集器最终运行并收集该对象而不将其放入终结队列。
显然,“Finalize”和“Dispose”经常需要做同样的事情。
但现在又出现了一个问题。假设您处置了一个对象。现在它不需要最终确定。最终确定是昂贵的;它使死对象存活的时间比它需要的时间长得多。因此,传统上在dispose对象时,Dispose的实现不仅会关闭非托管资源,还会将该对象标记为“该对象已经finalized,不要再finalize”。这样,它会欺骗垃圾收集器,使其不将对象放入终结队列。
因此,让我们回答您的具体问题:
调用 GC.SuppressFinalize 的对象会发生什么?
当对象死亡时,垃圾收集器将简单地回收对象的内存,而不会将对象放入终结器队列中。
我知道这意味着 GC 不会调用此类对象的终结器
GC从不调用终结器。终结器线程是唯一调用终结器的东西。
这些物体什么时候会真正被破坏?
目前尚不清楚您所说的“破坏”是什么意思。如果您的意思是“终结器何时运行?” 答案是“从不”,因为您说要禁止最终确定。如果您的意思是“托管堆中的内存何时被回收?”,答案是“一旦对象被垃圾收集器识别为死”。这将比正常情况更早发生,因为终结器队列不会使对象保持活动状态。