Finalization is expensive. It has the following costs…<snip of 2, 930 additional words>… Subject to all of the above, we guarantee that we will dequeue your object and initiate a call to the Finalize method. We do not guarantee that your Finalize method can be JITted without running out of stack or memory. We do not guarantee that the execution of your Finalize method will complete without being aborted. We do not guarantee that any types you require can be loaded and have their .cctors run. All you get is a “best effort” attempt.
I’m becoming more and more convinced that the guideline for non-deterministic finalization should be: avoid it. The only thing you need finalization for are non-memory resources: file handles, database connections, etc. You can either exercise a little diligence so as to manually track and clean up these things or you can subject yourself to the complicated algorithms described by Brumme. If I were managing a programming team, there’s no way that I’d trust junior programmers to read Brumme’s post and then tell them “Okay, make sure we don’t run out of database handles.” Nuh-uh. I’d say: “Refactor your non-memory resources into xPool objects and deterministically finalize them using try…finally or using statements and the IDisposable pattern.” Learning that might be more complex than reading Brumme’s post, but the results are much easier to review and duplicate.
The people who really get a huge benefit from non-deterministic finalization are library writers, who finally have a guarantee that sometime before the process ends, they’ll get a final shot at cleaning up resources. Even they, I think, should strive for explicit deterministic finalization and use the finalizer as a “last chance” effort to trigger clean-up.