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

587 Views Asked by At

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?

3

There are 3 best solutions below

2
Jon Skeet On BEST ANSWER

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.

0
qbik On

C# (and .Net Framework in general) uses Garbage Collector to manage memory, so you shouldn't need to worry about that. If you're coming from c++, this might be feel a little strange at first, but GC does it's job pretty well. Finalizers are called by Garbage Collector and the docs explicitly say:

The programmer has no control over when the finalizer is called because this is determined by the garbage collector.

In case you have a class that is heavy on resources and you want to control when the resources are freed, you should use IDisposable and using statement.

1
ikkentim On

Calling the finalizer is done by the garbage collector. To have perfect control over unmanaged resources, use a disposable pattern

class MyResource : IDisposable
{
  ~MyResource()
  {
    Dispose(false);
  }

  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this); // don't finalize after the object has already been disposed of
  }

  protected void Dispose(bool disposing)
  {
    if(disposing)
    {
      // TODO: Dispose managed resources
    }

    // TODO: Dispose unmanaged resources
  }
}

// when using
using(var resource = new MyResource())
{
  // ... use resource
} // when out of "using" scope, it will be disposed