即使对象超出范围,Dotnet 也不会调用其终结器。那么如何释放非托管资源呢?

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

我尝试使用以下代码

[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
        }
    }

for 循环内创建的 Node class 的每个对象在离开 for 循环作用域(评论为 "this scope")后不会被破坏。仅当 Main 方法的作用域结束时才调用它。是否可以在 for 循环作用域结束时自动调用 ~Node?

在执行上述代码时,我在调试中得到以下信息 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

这表明最先构造的对象最后被破坏。如果发生这种情况,当我 运行 数以千计的项目循环时会发生什么? 它会消耗我所有的记忆吗?

如何完美释放我的非托管资源?

TL;DR:如果可能,去掉终结器,相信垃圾收集器会做正确的事情。

最终确定是不确定的。重要的是要了解 objects 不会超出范围;对象没有开始的范围。 变量超出范围,不会触发任何东西。

通常,垃圾收集器只在需要时运行。无法保证调用终结器的顺序或调用它们的时间。 (虽然您可以请求垃圾收集器运行,但这通常是个坏主意,而且几乎没有任何保证。)

不过,在您自己的终结器中使用终结器几乎总是一个坏主意 类:它会延迟实际的垃圾收集,而且无论您在终结器中做什么,几乎总是有更好的方法。

C#(和一般的 .Net Framework)使用 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 由垃圾收集器调用,文档明确说明:

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

如果您有一个 class 占用大量资源并且您想控制何时释放资源,您应该使用 IDisposableusing 语句。

调用终结器由垃圾收集器完成。要完美控制非托管资源,请使用一次性模式

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