避免在C#中使用析构函数Finalizer
Contents
Eric Lippert在When everything you know is wrong part one和part two中列举了一堆关于Finalizer的错误认识。
- 把一个变量赋值为
null
会调用它之前指向的对象的Finalizer。 - 调用一个对象的
Dispose()
函数会调用它的Finalizer。 - 调用
GC.Collect()
会调用Finalizer。 - 只要一个对象有Finalizer,Finalizer就一定会被调到。
- 只要一个对象有Finalizer,并且没有调过
SuppressFinalize
,Finalizer就一定会被调到。 - 没有被任何对象引用的对象的Finalizer就一定会被调到。
- 如果一个对象被GC认为没有被任何对象引用,它的Finalizer就一定会被调到。
- 如果对象已经被放到了finalized queue,并且finalize线程已经被调度了,那它的Finalizer就一定会被调到。
- 如果对象已经被放到了finalized queue,并且finalize线程已经被调度了,并且finalizer都运行的很快,而且没有抛出异常,那它的Finalizer就一定会被调到。
- 局部变量只有在出了作用域它的Finalizer才可能会被调到。关于这个,可以参见我的博文谁动了我的timer?C#的垃圾回收和调试
- Finalizer不可能被调用多次。看看这个API:GC.ReRegisterForFinalize。
- 被调到Finalizer的对象是死对象。
- 被调到Finalizer的对象是不能被别的对象在指到的。
- 运行Finalizer的线程就是创建这个对象的线程。通常都在Finalize线程释放,但是COM的STA对象必须在STA线程释放,这个可以参见我的博文如何判断C#的Finalizer线程有没有被阻塞。
- 运行Finalizer的线程就是GC线程。
- GC判断一个对象是死对象时就会调用它的Finalizer。
- Finalizer从来不会死锁。
- Fianlizer的运行顺序是可以预计的。
- 一个被调到Finalizer的对象可以安全访问其它对象。
- 运行Finalizer会释放内存。
- 被调到Finalizer的对象肯定是完全创建好的对象。
再次强调一下,上面列举的这些都是错误的。Finalizer的坑这么多,那就还是不要用了。