垃圾回收是否在 GC 之后立即运行.收集()

本文关键字:运行 收集 之后 GC 是否 | 更新日期: 2023-09-27 18:31:54

这个问题只是为了研究目的。

我读过很多关于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. 垃圾回收没有立即在第 2 行运行是对的吗?
  2. GC.Collect()只是一个请求进行垃圾回收,该垃圾回收不会立即在 row2 上运行。此行可能在 x 毫秒/秒内执行。在我看来,方法System.GC.Collect();只是对垃圾收集器说垃圾收集器应该运行垃圾收集,但真正的垃圾收集可能会在x毫秒/秒内发生

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

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

更新:

我已经阅读了这篇关于GC.Collect Method ()的 msdn 文章..但是,我不清楚何时开始真正清除未引用的物品。MSDN 说:

气相色谱。收集方法 () 强制立即对所有 代。

但是,在评论中,我读到了这个:

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

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

垃圾回收是否在 GC 之后立即运行.收集()

简短回答

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

长答案

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

但是,可以通过调用 GC.WaitForFullGCComplete() 来等待完整的 GC 完成,并在调用 GC.Collect()GC.WaitForPendingFinalizers()

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

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

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

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

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

回答你的观点(5):

GC 的文档。Collect() 状态:

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

因此,这将强制使用GC。

该文档还指出:

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

在这里使用"try"一词仅意味着即使运行了完整的 GC,也不一定会回收所有无法访问的内存。可能出现的原因有多种,例如,终结器可能会阻塞。

脚注

.Net 4.5 允许您指定GC.Collect()是否阻塞。

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

例如,请参阅此线程。

答案是这样的:默认情况下GC.Collect()将等待所有世代被GCed,但它不会等待挂起的终结器,这些终结器总是在单独的线程中执行。

因此,如果您不需要等待终结器,则只需调用GC.Collect(),而无需等待其他任何内容。

有两个 GC,从您的代码中,我相信您想知道工作站 GC。哪个通过在完整收集期间并发运行来最大程度地减少暂停?工作站 GC 使用第二个处理器并发运行集合,从而最大限度地减少延迟,同时降低吞吐量。我们应该只担心服务器 GC 没有正常工作时的 GC 行为。如果按照工作站 GC 在代码中添加 GC.collect(),则在服务器 GC 上可能毫无用处。

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

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

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