Finalize方法在垃圾回收结束时被调用,下面有5种事件会导致开始垃圾回收
1.第0代已满 第0代已满,垃圾回收会自动开始。该事件是目前导致Finalize方法被调用的最常见的一种方式,因为虽然应用程序的运行并分配新对象,这个事件会自然而然的发生。
2.代码显示调用System.GC的静态方法Collect 代码可以显示的请求CLR执行垃圾回收。虽然Microsoft强烈建议不要这样做,但某些时候还是必要的。
3.Windows报告内存不足 CLR内部使用Win32 CreateMemoryResourceNofification和QueryMemoryResourceNotification函数来监视系统的整体内存。如果Windows报告内存不足,CLR将强制执行垃圾回收,尝试释放已经死亡的对象,从而减少进程工作集的大小。
4.CLR卸载AppDomain 一个AppDomain被卸载时,CLR认为该AppDomain中不再存在任何根,因此会对所有代码的对象执行垃圾回收。
5.CLR关闭 一个进程正常终止时 ,CLR就会关闭。在关闭过程中,CLR会认为该进程中不存在任何根,因此会调用托管堆中所有对象的Finalize方法。
CLR使用了一个特殊的,专用的线程来调用Finalize方法。对于前4中事件,如果一个Finalize方法进入一个无线循环,这个特殊的线程会被阻塞,其他Finalize方法得不到调用。因此应用程序永远都不能回收由终结器的对象占用的的内存 ,只要应用程序运行,就会一直泄露内存。
对于第5中事件。每个Finalize大约都有2秒的时间返回,如果在2秒内没有返回,CLR就直接杀死该进程。不会调用更多的Finalize方法。另外如果调用所有对象的Finalize方法的时间超过40秒,CLR也会杀死进程。
以本章前面给出的GCBeep类型为例。如果一个GCBeep对象由于前三种事件被终结。就会在终结过程中构建一个新的GCBeep。这是合理的,因为应用程序继续运行,后面将发生更多的垃圾回收,但是,如果GCBeep对象由于第4种和第5种事件终结,就不能再构造新的GCBeep对象,否则在appdomain卸载或CLR关闭过程中,还会多余的创建对象。创建这些多余的对象后,CLR将继续调用他们的Finalize方法,被迫做大量无用功。
为了阻止构造新的GCBeep对象,GCBeep的Finalize方法调用了AppDomain的IsFinalizingForUnload方法,同时查询了System.Environment的HasShutdownStarted属性,如果对象的Finalize方法时由于AppDomain卸载而被调用,IsFinalizingForUnload方法将返回true。如果对象的Finalize方法时由于对象的终止而被调用,HasShutdownStarted属性返回true.