0

我尝试使用以下代码

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

Node在循环内创建的类的每个对象for在离开 for 循环范围后都不会破坏(注释为“此范围”)。只有在 Main 方法的范围结束时才会调用它。当 for 循环范围结束时,是否可以自动调用 ~Node?

在执行上述代码时,我在调试窗口中得到以下信息。

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

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

我怎样才能完美地释放我的非托管资源?

4

3 回答 3

6

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

最终确定是不确定的。了解对象不会超出范围很重要。一个对象没有开始的范围。变量超出范围,这不会触发任何事情。

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

但是,在您自己的类中使用终结器几乎总是一个坏主意:它会延迟实际的垃圾收集,并且几乎总是有更好的方法来处理您在终结器中所做的任何事情。

于 2018-09-12T06:44:45.797 回答
1

C#(和一般的 .Net 框架)使用垃圾收集器来管理内存,因此您不必担心这一点。如果你来自 c++,一开始可能会觉得有点奇怪,但是 GC 做得很好。终结器由垃圾收集器调用,文档明确表示:

程序员无法控制何时调用终结器,因为这是由垃圾收集器决定的。

如果你有一个资源很重的类并且你想控制何时释放资源,你应该使用IDisposableand using声明。

于 2018-09-12T06:49:35.557 回答
-1

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

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
于 2018-09-12T07:14:18.173 回答