Finalizer is not called after an unhandled exception, even with CriticalFinalizerObject

I have a test code:

public class A : CriticalFinalizerObject { ~A() { File.WriteAllText("c:\\1.txt", "1z1z1"); } } class Program { static void Main(string[] args) { A a = new A(); throw new Exception(); } } 

At first, I tried to run it without calling A from CriticalFinalizerObject. After completing this program, Finalizer is not called. This surprised me, because I thought it was more deterministic, but everything was fine. Then I read about CriticalFinalizerObject, which ensures that their finalizers are called. I got from him A. Guess what. It is still not running. What am I doing / misunderstanding?

(Please do not write obvious things about the fact that the garbage collector is not deterministic, I know that. This is not so, since the program is completed, and I realized that I could safely clear it after a beautiful, unhandled managed exception.)

+7
source share
2 answers

First, read about CriticalFinalizerObject on MSDN , we can read that:

In classes derived from the CriticalFinalizerObject class, the common CLR runtime ensures that all critical exit codes are given the opportunity to execute if the finalizer follows the CER rules, even in situations where the CLR forces the application domain to be unloaded or interrupts the thread.

The main word here is UNLOAD .

Secondly, let's read MSDN again, this time about exceptions in managed threads:

If these exceptions are not handled in the main thread or in threads that have entered runtime from unmanaged code, they go fine, causing the application to terminate .

The main word is TERMINATION .

So, when there is an unhandled exception in the main thread, the application terminates, but CriticalFinalizerObject helps only when unloading the domain.

For example, CriticalFinalizerObject can help in this situation:

 // Create an Application Domain: AppDomain newDomain = AppDomain.CreateDomain("NewApplicationDomain"); // Load and execute an assembly: newDomain.ExecuteAssembly(@"YouNetApp.exe"); //Unload of loaded domain AppDomain.Unload(newDomain); 

This is a situation where the domain has been unloaded, and CriticalFinalizerObject guarantees you that your finalizer will be called.

In your situation with the completion of the application, you can try to subscribe to

 AppDomain.CurrentDomain.UnhandledException 

and manually complete your objects.

UPD: Jeffrey Richter in his book "CLR through C #" wrote about CriticalFinalizerObject that it is for situations when you send your code, for example, SQLServer, which can run C # as procedures. In this case, CriticalFinalizerObject will help you clear your object if SQLServer unloads your Domain library. Also, CriticalFinalizerObject is designed for situations where you need in the finalizer of an object to call the method of another object, since CriticalFinalizerObject guarantees you that its finalizer will be called after the finalizers of all objects are not CriticalFinalizerObject.

+9
source

Ok Wrote a simple test. If I have an exception in the constructor where I create my object A, I really can’t get finalizer to work, but when I created the object not in the constructor of another class, but in a different method, and then threw an exception, it worked.

So, I suspect that if the constructor never finished creating the class, and its creation stopped, the object was never created, the stack was cleared, the objects were deleted from the heap without completion, as never had happened.

This is my hunch. But to solve this problem, I would wrap the critical constructive code by creating objects in try-catch-finally and clearing the call code explicitly.

Example: It worked

  public Form1() { InitializeComponent(); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); var a = new A(); throw new Exception(); } } 

Is not

 public Form1() { InitializeComponent(); var a = new A(); throw new Exception(); } } 
0
source

All Articles