C# GC.Collect()的用途

C# GC.Collect()的用途,c#,.net,garbage-collection,operating-system,C#,.net,Garbage Collection,Operating System,我不熟悉.NET和CLR,只是一个关于CLR垃圾收集的问题 我的教科书描述了使用GC的目的之一。Collect()是 您的应用程序刚刚完成分配大量对象 您希望尽快删除尽可能多的已获取内存。 下面是我的代码: Console.WriteLine("Estimated bytes on heap: {0}", GC.GetTotalMemory(false)); Car refToMyCar = new Car(); Console.WriteLine("\nGeneration of refToM

我不熟悉.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()似乎只标记从第0代到第1代由refToMyCar指向的项,没有任何内容是“空闲”的,因为第1代表示一个在垃圾收集中幸存的对象。假设在GC.Collect()之前,堆上还有10mb的可用大小(例如总共100mb),而在GC.Collect()之后,堆上仍然只有10mb的可用大小,那么调用GC.Collect()有什么意义呢?我们不希望可用大小为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));

为什么refToMyCar是第2代,它是一个经过多次垃圾收集器扫描后仍然存在的对象?我们还没有调用任何隐式或显式GC.Collect()?

在您的
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));
发生这种情况的原因是内存管理的执行方式,如果您在代码中的某个地方引用了变量,它将继续存在,并且它的生成可能会更新。这就是为什么您应该小心闭包和树结构

输出:

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

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

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

当然,这两种行为都在中进行了描述


处理
GC.Collect
的基本规则非常简单-不要使用它。很少有情况下这样做会有任何好处,而且大多数时候,你只是在浪费CPU、内存,让对象比其他情况下存活的时间更长。

不客气,但你每天都会问很多次这些问题,所有这些问题都可以研究(你似乎有能力)而不是在这里问,这些问题中的大多数以前都被问过很多次,通常会有博客和其他非现场资源,它们会给你比这个问答形式更深入的知识和更好的理解。你实际上在一个问题中问了3个问题,这是不受欢迎的
我是.NET和CLR的新手,冒着听起来粗鲁的风险,等一年再调查玩
GC.Collect()
。您需要手动调用它是非常罕见的,而且在您正在进行的开发中,您不太可能遇到这些场景之一。现在,您的心智模型应该是“让GC做它自己的事情——我不去管它”。我们可以为你的每一个问题提供详细的答案,但老实说,这会让你现在不知所措。GC的细节花了好几年才在我的脑海中形成。你需要知道的一切都在这里
我有一年的C#开发经验
1个月或1年-这没有什么区别。再等一两年<代码>好的,我只是想知道为什么refToMyCar是第2代,它不应该是第0代吗?
它在哪里承诺会是第0代?另外,请阅读大对象堆。再一次,我强烈建议把这件事放在一边一两年,然后再回来。
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