Reputation: 1694
I've written simple test program:
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; }
}
}
I make snapshots using build-in Visual Studio 2017 diagnostic tools. When a1 - a5 variables are initialized, there are 25 A instances in the heap, as expected:
But after variables a1-a5 become equal to null, even after additional calls of GC.Collect method, the heap still contains A instances. And more calls of GC.Collect doesn't reduce A objects in the heap. Why? Is there way to force collect ALL unused objects?
EDIT: I've switched to the Release configuration and I see that in the heap there are 15 objects after initializing a1-a5 variables:
And after GC.Collect calls still remains 14 objects.
Upvotes: 2
Views: 289
Reputation: 116401
I don't know how VS handles this, but if I run your code using a release build I don't see any instances of A
on the heap when inspecting it using SOS.
To see for yourself, download WinDbg. Then attach to your process and load SOS.
.loadby sos clr
You can dump the entire heap using !dumpheap -stat
or just the type using !dumpheap -type GCTest.A
.
You should see instances going up as they are created. Once GC has reclaimed the instances you shouldn't see any instances of A
on the heap.
Here's the output following the first GC after all references are nulled.
0:001> !dumpheap -type GCTest.A
Address MT Size
Statistics:
MT Count TotalSize Class Name
Total 0 objects
Upvotes: 4