Search code examples
c#unmanagedunmanagedresources

Dotnet not calling its finalizer even if the object goes out of scope. How to release unmanaged resources then?


I tried with the following code

[DllImport("Core.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr CreateNode();

[DllImport("Core.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void ReleaseNode(IntPtr handle);

 class Node
{
    IntPtr nativePtr;
    public int id;
    public Node(int i)
    {
        nativePtr = CreateNode();
        id = i;
        Debug.WriteLine("Constructed " + id);
    }

    ~Node()
    {
        ReleaseNode(nativePtr);
        Debug.WriteLine("Destructed " + id);
    }
}

    class Program
    {
        static void Main(string[] args)
        {

            for (int i = 0; i < 10; i++)
            {
                Node n = new Node(i);
            } //this scope
        }
    }

Each objects for Node class created inside for loop is not destructing after leaving the for loop scope (commented as "this scope"). it is called only when the scope of the Main method ends. is it possible to call the ~Node automatically when the for loop scope ends?

while executing the above code I'm getting the following in the debug window.

Constructed 0
Constructed 1
Constructed 2
Constructed 3
Constructed 4
Constructed 5
Constructed 6
Constructed 7
Constructed 8
Constructed 9
Destructed 9
Destructed 0
Destructed 8
Destructed 7
Destructed 6
Destructed 5
Destructed 4
Destructed 3
Destructed 2
Destructed 1

this shows that the object constructed first is getting destructed at the last. If this happens, what will happen, when im running thousands of items in loop? Will it consume all my memory?

How can i release my unmanaged resources perfectly?


Solution

  • TL;DR: Get rid of the finalizer if possible, and just trust the garbage collector to do the right thing.

    Finalization is non-deterministic. It's important to understand that objects don't go out of scope; an object doesn't have a scope to start with. Variables go out of scope, and that doesn't trigger anything.

    Usually, the garbage collector just runs when it needs to. There is no guarantee about the order in which finalizers will be called, or when they'll be called. (While you can request that the garbage collector runs, that's usually a bad idea and has few guarantees anyway.)

    It's almost always a bad idea to have a finalizer in your own classes though: it delays actual garbage collection, and there are almost always better approaches to whatever you'd do in the finalizer.