垃圾收集在GC.Collect()之后立即运行吗?

时间:2016-01-25 08:38:44

标签: c# .net garbage-collection garbage

这个问题仅供研究之用。

我读过很多关于C#的书,我总会想到这个问题。据我所知,C#是托管代码,当CLR决定何时运行垃圾收集时,会发生所有垃圾收集。让我们开始吧。

让我们假设我有一个简单的课程Student

public class Student
{
    public int IdStudent { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
}
class Program
{
      static void Main(string[] args)
      {
This is row1:    Person person = new Person() {IdPerson=1, Name="Bill", SurName="Collins"};
This is row2:    System.GC.Collect();
This is row3:    string str="Hello World!";
    }        
}

请批准或拒绝我的假设:

  1. 我是否认为垃圾收集不会立即在 row2
  2. 上运行
  3. GC.Collect()只是一个请求来进行垃圾收集,它不会立即在第2行运行。此行可能以x毫秒/秒执行。 在我看来,方法System.GC.Collect();只是告诉垃圾收集器垃圾收集器应该运行垃圾收集,但真正的垃圾收集可能会以x毫秒/秒发生

  4. 只有垃圾收集器知道何时运行垃圾收集。如果第0代中有可用空间,则第2行中不会进行垃圾收集: row2: System.GC.Collect();

  5. 由于我们在托管环境中编程,因此无法立即运行垃圾收集,只有CLR决定何时运行垃圾收集。垃圾收集可以x毫秒/秒运行或者垃圾收集可能无法运行因为在调用方法GC.Collect()之后,第0代中有足够的空间来创建新对象。程序员可以做的只是让CLR按方法GC.Collect()运行垃圾收集。

  6. 更新

    我看过this msdn article about GC.Collect Method ().。但是,当我开始真正清除未引用的对象时,我不清楚。 MSDN说:

      

    GC.Collect Method()强制立即对所有垃圾进行收集   代。

    但是,在备注中,我读过这篇文章:

      

    使用此方法尝试回收所有无法访问的内存。它   执行所有代的阻塞垃圾收集。

    1. 我对此感到困惑“使用此方法尝试”,我认为垃圾收集可能不会发生因为 CLR 决定那里有足够的空间来创建新对象。我是对的吗?

2 个答案:

答案 0 :(得分:5)

简短回答

调用GC.Collect()将执行完整的垃圾收集并等待它完成,但它不会等待任何挂起的终结器运行。

LONG ANSWER

您在假设中部分正确,因为运行终结器的GC在一个或多个后台线程中运行。 (但请参阅本答复末尾的脚注。)

但是, 可以在您致电GC.Collect()后致电GC.WaitForFullGCComplete()GC.WaitForPendingFinalizers()等待完整的GC完成:

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

但是,请注意未指定运行终结器的线程,因此无法保证此方法将终止。

请注意,您通常不应以这种方式使用GC;我假设您有一个特殊情况需要解决,或者您是出于研究目的而这样做。

我见过的唯一有效案例是应用程序正在关闭,并且您希望(尝试)确保所有终结器都已运行 - 例如,因为它们将刷新日志文件等。

如上所述,这仍然不能保证所有终结器都已运行;它是你能做的最好的事情。

回答你的观点(5):

GC.Collect()的文档说明:

强制所有世代立即进行垃圾收集。

因此强制使用GC。

文档还说明:

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

使用"试试"这仅仅意味着即使运行完整的GC,也不一定会回收所有无法访问的内存。有几个原因可能发生,例如,终结器可能会阻止。

<强>脚注

.Net 4.5 allows you to specify whether GC.Collect() is blocking or not

事实上,GC.Collect()的文档指出它执行所有代的阻塞垃圾收集,这似乎与我上面的陈述相矛盾。但是,对于是否真的存在这种情况似乎存在一些混淆。

请参阅示例this thread

答案是这样的:GC.Collect() 默认等待所有代的GCed,但它不会等待挂起的终结器,它总是在一个单独的线程中执行。 / p>

因此,如果您不需要等待终结者,您只需要致电GC.Collect(),而无需等待其他任何事情。

答案 1 :(得分:1)

有两个GC,从您的代码中,我相信您想了解Workstation GC。通过在完整集合期间同时运行来最小化暂停? Workstation GC使用第二个处理器同时运行集合,最大限度地减少延迟,同时降低吞吐量。如果服务器GC没有正常工作,我们应该只担心GC行为。如果您根据工作站GC在代码中添加GC.collect(),那么在服务器GC上可能没用。

服务器GC旨在实现最大吞吐量,并以非常高的性能进行扩展。服务器上的内存碎片比工作站上的问题严重得多,使垃圾收集成为一个有吸引力的主张。在单处理器场景中,两个收集器的工作方式相同:工作站模式,没有并发收集

  

我很困惑这个使用这个方法来尝试,我认为垃圾收集可能不会发生,因为CLR决定有足够的空间来创建新对象。我是对的吗?

对于workstaion GC,GC.Collect将尽快开始收集,您可以安全地假设它立即收集。