.Net为什么GC无法从堆中删除对象?

时间:2018-11-09 07:38:43

标签: c# .net garbage-collection

我编写了简单的测试程序:

namespace GCTest {
class Program {
    static void Main(string[] args) {
        var a1 = new A();
        a1.AProperty = new A();
        a1.AProperty.AProperty = new A();
        a1.AProperty.AProperty.AProperty = new A();
        a1.AProperty.AProperty.AProperty.AProperty = new A();
        Console.WriteLine("a1 created");
        Console.ReadKey();

        var a2 = new A();
        a2.AProperty = new A();
        a2.AProperty.AProperty = new A();
        a2.AProperty.AProperty.AProperty = new A();
        a2.AProperty.AProperty.AProperty.AProperty = new A();
        Console.WriteLine("a2 created");
        Console.ReadKey();

        var a3 = new A();
        a3.AProperty = new A();
        a3.AProperty.AProperty = new A();
        a3.AProperty.AProperty.AProperty = new A();
        a3.AProperty.AProperty.AProperty.AProperty = new A();
        Console.WriteLine("a3 created");
        Console.ReadKey();

        var a4 = new A();
        a4.AProperty = new A();
        a4.AProperty.AProperty = new A();
        a4.AProperty.AProperty.AProperty = new A();
        a4.AProperty.AProperty.AProperty.AProperty = new A();
        Console.WriteLine("a4 created");
        Console.ReadKey();

        var a5 = new A();
        a5.AProperty = new A();
        a5.AProperty.AProperty = new A();
        a5.AProperty.AProperty.AProperty = new A();
        a5.AProperty.AProperty.AProperty.AProperty = new A();

        Console.WriteLine("a5 created");
        int a1Gen = GC.GetGeneration(a1);
        Console.WriteLine("a1 generation: " + a1Gen);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        a1Gen = GC.GetGeneration(a1);
        Console.WriteLine("a1 generation: " + a1Gen);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        a1Gen = GC.GetGeneration(a1);
        Console.WriteLine("a1 generation: " + a1Gen);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        a1Gen = GC.GetGeneration(a1);
        Console.WriteLine("a1 generation: " + a1Gen);
        Console.ReadKey();

        a1 = a2 = a3 = a4 = a5 = null;
        Console.WriteLine("a1-a5 are null");
        Console.ReadKey();

        Console.WriteLine("GC");
        GC.Collect(2, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();
        Console.ReadKey();

        Console.WriteLine("GC");
        GC.Collect(2, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();
        Console.ReadKey();

        Console.WriteLine("GC");
        GC.Collect(2, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();
        Console.ReadKey();

        Console.WriteLine("GC");
        GC.Collect(2, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();
        Console.ReadKey();
    }
}

class A {
    int[] arr = new int[1000];
    public A() {
        arr[0] = 1;
    }

    public A AProperty { get; set; }
}

}

我使用内置的Visual Studio 2017诊断工具制作快照。 初始化a1-a5变量后,按预期,堆中有25个A实例:

enter image description here

但是在变量a1-a5等于null之后,即使在附加调用GC.Collect方法之后,堆仍然包含A实例。并且更多的GC.Collect调用不会减少堆中的A对象。为什么?有办法强制收集所有未使用的对象吗?

enter image description here

编辑: 我已经切换到Release配置,并且看到在初始化a1-a5变量后堆中有15个对象:

enter image description here

在GC.Collect调用之后,仍然保留了14个对象。

1 个答案:

答案 0 :(得分:4)

我不知道VS如何处理此问题,但是如果我使用发行版版本运行您的代码,则在使用SOS检查它时,堆上不会出现A的任何实例。

要亲自查看,download WinDbg。然后附加到您的进程并加载SOS。

.loadby sos clr

您可以使用!dumpheap -stat转储整个堆,也可以使用!dumpheap -type GCTest.A转储整个堆。

在创建实例时,您应该看到实例正在上升。一旦GC回收了实例,您就不会在堆上看到A的任何实例。

这是所有引用都为空之后的第一个GC之后的输出。

0:001> !dumpheap -type GCTest.A
 Address       MT     Size

Statistics:
      MT    Count    TotalSize Class Name
Total 0 objects