【问题标题】:The purpose of GC.Collect()GC.Collect() 的目的
【发布时间】:2019-10-25 10:25:35
【问题描述】:

我是 .NET 和 CLR 的新手,只是关于 CLR 垃圾收集的问题。

我的教科书描述了使用 GC.Collect() 的目的之一是

您的应用程序刚刚分配完大量的对象 并且您希望尽快删除尽可能多的已获取内存。

下面是我的代码:

Console.WriteLine("Estimated bytes on heap: {0}", GC.GetTotalMemory(false));
Car refToMyCar = new Car();
Console.WriteLine("\nGeneration of refToMyCar is: {0}", GC.GetGeneration(refToMyCar));
GC.Collect(0, GCCollectionMode.Forced);
Console.WriteLine("\nGeneration of refToMyCar is: {0}", GC.GetGeneration(refToMyCar));
Console.WriteLine("Estimated bytes on heap: {0}", GC.GetTotalMemory(false));

结果是:

Estimated bytes on heap: 29900

Generation of refToMyCar is: 0

Generation of refToMyCar is: 1

Estimated bytes on heap: 39648

所以这是我的问题:

1- GC.Collect() 似乎仅将 refToMyCar 指向的项目从第 0 代标记到第 1 代,没有任何东西是“免费的”,因为第 1 代意味着一个在垃圾收集中幸存下来的对象。假设在 GC.Collect() 之前,堆上还有 10mb 可用大小(例如总共 100mb),而在 GC.Collect() 之后,堆上仍然只剩下 10mb,那么调用 GC 有什么意义。收藏()?我们不希望可用大小为 100mb 100% 可用吗?

编辑: 放弃我之前的问题

如果我把它改成对象数组,那就更奇怪了:

Console.WriteLine("Estimated bytes on heap: {0}", GC.GetTotalMemory(false));
object[] refToMyCar = new object[50000];
for (int i = 0; i < 50000; i++)
   refToMyCar[i] = new object();
Console.WriteLine("\nGeneration of refToMyCar is: {0}", GC.GetGeneration(refToMyCar));       
Console.WriteLine("Estimated bytes on heap: {0}", GC.GetTotalMemory(false));

输出是:

Estimated bytes on heap: 29900

Generation of refToMyCar is: 2
Estimated bytes on heap: 836140
Press any key to continue . . .

为什么 refToMyCar 是第 2 代,它是一个在不止一次垃圾收集器扫描中幸存下来的对象?我们还没有调用任何隐式或显式 GC.Collect() 吗?

【问题讨论】:

  • No Disrespect 但是你每天要问这些问题很多次,所有这些问题都可以研究(看起来你有能力)而不是在这里问,大多数这些问题之前已经被问过很多次了,通常有博客和其他站外资源,比这种问答形式能给你更深入的知识和更好的理解
  • 另外你居然一问三问,不服
  • I'm new to .NET and CLR 冒着听起来粗鲁的风险,请等待一年后再调查GC.Collect()。您需要手动调用它的情况非常少见——而且您不太可能在您正在进行的开发中遇到其中一种情况。现在,你的心智模型应该是“让 GC 做它的事——我会不管它的”。我们可以为您的每一个问题提供详细的答案 - 但老实说,它现在会让您不知所措。 GC 的更多细节花了数年时间才在我的脑海中浮现。
  • I have one year of C# development experience 1 个月或 1 年 - 没有区别。再等一两年。 ;) OK, I just want to know why refToMyCar is generation 2, shouldn't it be generation 0? 它在哪里承诺它将成为第 0 代?另外,请阅读 Large Object Heap。同样,我强烈建议将其搁置一两年,然后再重新考虑。

标签: c# .net garbage-collection operating-system


【解决方案1】:

不允许垃圾收集器收集仍然被引用的对象。由于您保留对refToMyCar 的引用以便稍后生成它,因此无法收集它。如果你想观察被回收的对象,你可以使用WeakReference来代替。此外,您需要在没有调试器的情况下运行 - 为了帮助调试,调试器使所有引用都能够存活,直到它们超出范围(即块/方法体结束)。

在第二种情况下,您正在分配一个大对象。 .NET 会将它们放在一个特殊的堆上,即大对象堆。这些对象有特殊的规则——它们总是被认为是第二代,它们不能被移动(除非你明确要求 GC 这样做)。您需要特别注意大型物体。

当然,documentation 中描述了这两种行为。

处理GC.Collect 的基本规则非常简单——不要使用它。在极少数情况下这样做有任何好处,而且大多数情况下,您只是在浪费 CPU、内存并使对象的生存时间更长

【讨论】:

    【解决方案2】:

    在你的GC.Collect() 之后,你已经使用了你想要收集的变量:

    Console.WriteLine("Estimated bytes on heap: {0}", GC.GetTotalMemory(false));
    Car refToMyCar = new Car();
    Console.WriteLine("\nGeneration of refToMyCar is: {0}", GC.GetGeneration(refToMyCar));
    GC.Collect(0, GCCollectionMode.Forced);
    
    // after removing reference to the variable it's been collected and will not survive to generation 1
    // Console.WriteLine("\nGeneration of refToMyCar is: {0}", GC.GetGeneration(refToMyCar));
    Console.WriteLine("Estimated bytes on heap: {0}", GC.GetTotalMemory(false));
    

    发生这种情况是因为内存管理的执行方式以及如果您在代码中的某个地方引用变量 - 它会继续存在并且它的生成可能会被更新。这就是为什么你应该小心闭包和树结构。

    输出:

    Estimated bytes on heap: 29988
    
    Generation of refToMyCar is: 0
    Estimated bytes on heap: 29448
    

    关于你的其他问题 - 我相信你不能仅仅因为你必须将程序本身存储在某个地方,静态类内存分配 - GC 就是一个很好的例子。

    【讨论】:

      猜你喜欢
      • 2012-05-11
      • 1970-01-01
      • 2022-01-10
      • 2012-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多