GC.Collect()

时间:2019-06-11 03:51:09

标签: 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 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()吗?

2 个答案:

答案 0 :(得分:3)

不允许垃圾收集器收集仍在引用的对象。由于您保留对refToMyCar的引用以在以后生成它,因此无法收集它。如果要观察回收的对象,可以改用WeakReference。另外,您需要在没有调试器的情况下运行-为了帮助调试,调试器可以做到这一点,以便所有引用都可以保留到超出范围(即块/方法主体结束)为止。

在第二种情况下,您正在分配一个大对象。 .NET会将它们放在特殊的堆(大型对象堆)上。这些对象具有特殊的规则-它们始终被视为第二代,并且无法移动(除非您明确要求GC这样做)。您需要特别注意大物体。

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

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

答案 1 :(得分:0)

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

关于其他问题-我相信您无法释放100%的堆内存,只是因为您必须将程序本身存储在某个地方,静态类内存分配-GC是一个很好的例子。