GC.Collect()不立即收集?

时间:2012-07-05 23:49:43

标签: c# garbage-collection destructor finalizer

在聊天讨论过程中,我编写了这个控制台应用程序。

代码:

using System;

class Program
{
    static void Main(string[] args)
    {
        CreateClass();
        Console.Write("Collecting... ");
        GC.Collect();
        Console.WriteLine("Done");
    }

    static void CreateClass()
    {
        SomeClass c = new SomeClass();
    }
}

class SomeClass
{
    ~SomeClass()
    {
        throw new Exception();
    }
}

结果:

Collecting... Done

Unhandled Exception: System.Exception: Exception of type 'System.Exception' was
thrown.
   at SomeClass.Finalize()

我希望应用程序在 Done打印之前崩溃

我不太关心如何制作它。我的问题是,为什么不呢?

4 个答案:

答案 0 :(得分:12)

无法在单个垃圾收集过程中收集具有终结器的对象。这些对象被移动到f-reachable队列,并保持在那里直到调用终结器。只有在那之后它们才能被垃圾收集。

以下代码更好,但你不应该依赖它:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

此外,即使是出于测试目的,在终结器中抛出异常对我来说似乎太残酷了。

此外,终结器的有趣副作用:如果在终结器中存储this引用(将其分配给某个静态变量),则具有终结器的对象仍然可以“自我复活”(有效地防止自身的垃圾收集)。

答案 1 :(得分:6)

您是否阅读过the documentation

  

使用此方法尝试回收所有无法访问的内存。

这不是一个命令,它是一个请求,可能会或可能不会按您的意愿运行。无论如何,这通常不是一个好主意(有时很多很小的短期对象是由某些过程创建的,在这种情况下,可能对调用GC.Collect有益,但这很少见。)

由于您似乎没有尝试解决实际问题,而是使用GC来解决这个问题,因此这是我提供的最佳建议。

答案 2 :(得分:3)

在最常见的垃圾收集器实现中,在垃圾收集周期中不能运行托管用户代码。 Finalize方法计为用户代码。虽然理论上系统可能在执行Finalize方法时冻结所有其他用户代码,但这种行为会增加多核系统上垃圾收集的表观成本,并且还会增加死锁的可能性。为了避免这些问题,系统不会将Finalize方法作为垃圾收集的一部分运行,而是构建一个需要运行Finalize方法的对象列表(该列表称为“可释放队列” “)。列表本身被认为是一个带根引用,因此,至少在系统从队列中检索可释放对象之前,任何由可释放队列中的对象引用的对象都将被视为强根,运行其{{ 1}}方法,并丢弃引用。

Microsoft关于最终化的早期文档非常混乱,因为它表明当这些方法运行时,可终结对象持有引用的对象可能不存在。实际上,所有这些对象都保证存在;不确定的是他们是否已经运行了Finalize方法。

答案 3 :(得分:0)

  1. GC.Collect()将那些未被引用的对象放到终结器队列中。
  2. 它将通过调用finalize方法清除终结器队列。
  3. 如果以上两点清楚,那么您的代码在静态方法中保存对SomeClass的引用。这意味着它在程序的主要方法执行之前仍然存在。

    如果您希望应用程序在打印“完成”之前崩溃,那么首先使SomeClass对象无效,然后调用GC.Collect。它会将你的对象放在终结器队列中,但GC也希望何时清除该队列。如果希望GC清除该队列并调用终结器,则调用GC.WaitForPendingFinalizers()。你的线程将一直等到你的终结器被调用,然后它将继续。我修改了你想要的输出代码。我没有抛出异常而是在终结器中打印了一个语句。

    class Program
        {
            static void Main(string[] args)
            {
                CreateClass();
                Console.Write("Collecting... ");
                GC.Collect();
                GC.WaitForPendingFinalizers();
                Console.WriteLine("Done");
    
                Console.ReadLine();
            }
    
            static void CreateClass()
            {
                SomeClass c = new SomeClass();
                c = null;
            }
        }
    
        class SomeClass
        {
            ~SomeClass()
            {
                Console.WriteLine("Finalized...");
            }
        }