当垃圾收集在运行时,析构函数如何调用[.NET]?

时间:2011-10-29 14:20:57

标签: c# .net garbage-collection

我正在尝试使用垃圾收集器,并在iCnt电话上发现奇怪的计数destructor。 这是代码: -

public class MyClass
{
    static long iCnt;
    public MyClass()
    {
        iCnt++;
    }

    ~MyClass()
    {
        iCnt--;
    }
    static public string ObjectCount
    {
        get
        {
            return iCnt.ToString();
        }
    }
}

下面的代码是创建对象,删除对象(通过分配到null去参考)并显式调用GC.Collect()。 我将所有内容都放在Timer interval = 100的Win表单中,计时器显示当前的iCnt计数。

public partial class Form1 : Form
{
    MyClass[] Objs;
    public Form1()
    {
        InitializeComponent();
    }

    private void btnCreateObjects_Click(object sender, EventArgs e)
    {
        Objs = new MyClass[1000];
        for (int i = 0; i < 1000; i++)
        {
            Objs[i] = new MyClass();
        }
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        label2.Text = MyClass.ObjectCount;
    }
    private void btnDeleteObjects_Click(object sender, EventArgs e)
    {
        for (int i = 0; i < 1000; i++)
        {
            Objs[i] = null;
        }
    }

    private void btnInvokeGC_Click(object sender, EventArgs e)
    {
        GC.Collect();
    }
}

现在创建对象,直到自动调用垃圾收集器(对我来说,它需要24次点击)。再次重复此操作。

我看到了iCnt&lt; 1000.这使我混淆了析构函数在这种情况下如何调用。 何时iCnt&lt; 1000,点击删除对象Objs[1000]。然后拨打GC.Collect(),将iCnt&lt; 0(非常奇怪)。

任何人都能解释一下这种行为。提前谢谢。

2 个答案:

答案 0 :(得分:5)

这是一个非常经典的线程错误。析构函数在终结器线程上运行,与递增计数的线程异步。您需要使用Interlocked.Increment()和Decrement()来安全地执行此操作。或使用 lock 关键字。

答案 1 :(得分:0)

我认为你真正需要做的是使用“使用”声明。像这样:

使用(foo = new bar()){... code ...}

使用时,您可以确定在代码终止时将发生dispose事件,即使它抛出异常。在你描述的情况下,C#垃圾收集器决定何时触发dispose事件(这就是调用你的析构函数)。因此,当对象坐在周围时没有对它们的引用但是没有相同的处理时,你可以有很长的时间。