Я написал простую тестовую программу:
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, как и ожидалось:
Но после того, как переменные a1-a5 станут равны нулю, даже после дополнительных вызовов метода GC.Collect, куча все еще содержит экземпляры A. И больше вызовов GC.Collect не уменьшают объекты A в куче. Зачем? Есть ли способ принудительно собрать ВСЕ неиспользованные предметы?
РЕДАКТИРОВАТЬ: я переключился на конфигурацию выпуска и я вижу, что в куче есть 15 объектов после инициализации переменных a1-a5:
А после вызовов GC.Collect по-прежнему остается 14 объектов.
Я не знаю, как VS справляется с этим, но если я запускаю ваш код, используя сборку релиза, я не вижу экземпляров A
в куче при проверке его с помощью SOS.
Чтобы убедиться в этом, скачайте 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