5

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

代码:

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

4 回答 4

12

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

以下代码更好,但无论如何您都不应该依赖它:

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

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

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

于 2012-07-05T23:55:41.653 回答
6

你读过文档吗?

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

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

由于您似乎不是在尝试解决真正的问题,而是在玩弄 GC,这是我必须提供的最佳建议。

于 2012-07-05T23:53:14.240 回答
3

在最常见的垃圾收集器实现中,在垃圾收集周期内不能运行任何托管用户代码。 Finalize方法算作用户代码。虽然理论上系统可以在Finalize方法执行时冻结所有其他用户代码,但这种行为会增加多核系统上垃圾收集的明显成本,并增加死锁的可能性。为避免这些问题,系统不会将Finalize方法作为垃圾回收的一部分运行,而是构建一个对象列表,这些对象需要Finalize方法运行(该列表称为“freachable queue”)。列表本身被认为是一个有根引用,因此任何被 freachable 队列中的对象引用的对象都将被认为是强根的,至少直到系统从队列中检索 freachable 对象,运行它的Finalize方法,并丢弃参考。

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

于 2012-07-06T15:11:16.923 回答
0
  1. GC.Collect() 将那些未被引用且具有终结器方法的对象放入终结器队列。
  2. 它将通过调用 finalize 方法来清除终结器队列。

如果上述两点很清楚,那么您的代码在静态方法中保存了对 SomeClass 的引用。这意味着它在程序的 main 方法执行之前仍然存在。

如果您希望您的应用程序在打印“完成”之前崩溃,则首先使您的 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...");
        }
    }
于 2013-10-09T11:43:59.677 回答