简体   繁体   中英

Is it possible to force C# finalizers be run in .NET Core on program exit?

I have a .NET Core C# class that wraps an unmanaged pointer and it should be freed on program exit along with other resource cleanup. However, the destructor is not being called. I have tried in both Debug and Release mode. I see that .NET Core apparently doesn't guarantee that destructors will be run, so what is a recommended workaround? IMO the main point of garbage collection is to avoid having the developer track references, so I find this behavior surprising, to say the least.

From MSDN : In .NET Framework applications (but not in .NET Core applications) , finalizers are also called when the program exits.

public Demo { 
  IntPtr _ptr;

  public Demo() 
  { 
    Console.WriteLine("Constructor");
    _ptr = /* P-invoke external function */ 

  ~Demo 
  {
    Console.WriteLine("Destructor");
    /*P-invoke ptr deletion */
  }
}

public static void Main() 
{ 
  Demo demo = new Demo();
  demo = null;
  GC.Collect();
}

Program output:

Constructor
<...>\Test.exe (process 7968) exited with code 0.

More changes are needed to improve the likelihood that the finalizer will be called.

Btw, Finalizer is never guaranteed to be called . If you want to gurantee the resources release, implement IDisposable and call Dispose() before the app/method/code block exit. Additionally to make Dispose() guaranteed to call (even if app crashes, except FailFast and StackOverflow) before exiting the code block, use try-finally or using statements.

Here's an example to play with.

public class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("[main] Constructing");
        MyDisposable m = new MyDisposable(0);
        MyMethod(1);
        Console.WriteLine("[main] Disposing [object 0]");
        m.Dispose();
        Console.WriteLine("[main] GC Collecting");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("[main] Done");
        Console.ReadKey();
    }

    private static void MyMethod(int i)
    {
        new MyDisposable(i);
    }
}

public class MyDisposable : IDisposable
{
    private int _id;

    public MyDisposable(int id)
    {
        _id = id;
        Console.WriteLine($"[object {_id}] Constructed");
    }

    private bool disposed = false;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                Console.WriteLine($"[object {_id}] Disposing by Dispose()");
            }
            else
            {
                Console.WriteLine($"[object {_id}] Disposing by ~Finalizer");
            }
            Console.WriteLine($"[object {_id}] Disposed");
            disposed = true;
        }
        else
            Console.WriteLine($"[object {_id}] Already disposed!");
    }

    ~MyDisposable()
    {
        Dispose(false);
    }
}

Output

[main] Constructing
[object 0] Constructed
[object 1] Constructed
[main] Disposing [object 0]
[object 0] Disposing by Dispose()
[object 0] Disposed
[main] GC Collecting
[object 1] Disposing by ~Finalizer
[object 1] Disposed
[main] Done

Some read: Using objects that implement IDisposable .

The official Cleaning up unmanaged resources states:

If your types use unmanaged resources, you should do the following:

Implement the dispose pattern. (...)

In the event that a consumer of your type forgets to call Dispose, provide a way for your unmanaged resources to be released. There are two ways to do this:

  • Use a safe handle to wrap your unmanaged resource. This is the recommended technique. Safe handles are derived from the System.Runtime.InteropServices.SafeHandle abstract class and include a robust Finalize method. When you use a safe handle, you simply implement the IDisposable interface and call your safe handle's Dispose method in your IDisposable.Dispose implementation. The safe handle's finalizer is called automatically by the garbage collector if its Dispose method is not called.

(...)

Implement a Dispose method contains:

  • ' the general pattern for implementing the dispose pattern for a base class that uses a safe handle '; and
  • ' the general pattern for implementing the dispose pattern for a base class that overrides Object.Finalize '.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM