【问题标题】:GC.Collect() not collecting immediately?GC.Collect() 没有立即收集?
【发布时间】:2012-07-06 10:24:06
【问题描述】:

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

代码:

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 打印之前崩溃。

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

【问题讨论】:

标签: c# garbage-collection destructor finalizer


【解决方案1】:

你读过the documentation吗?

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

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

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

【讨论】:

    【解决方案2】:

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

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

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

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

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

    【讨论】:

    • 我发现如果我睡了大约 100 毫秒,那么终结器会在我到达 WriteLine 之前运行。终结器很快被调用(但不够快)。你对这种行为有任何意见吗?
    • 是的,这是因为终结器是在单独的线程上调用的。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:
      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...");
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-19
        • 1970-01-01
        • 2023-03-15
        • 2011-01-25
        • 2013-11-07
        • 2015-10-09
        相关资源
        最近更新 更多