4

I run the following code

        StringBuilder sb = new StringBuilder();
        Stack  stack = new Stack();
        SynchronizationContext sc = new SynchronizationContext();

        GC.Collect();
        Console.WriteLine("GC. First Execution.");

        stack = null;

        GC.Collect();
        Console.WriteLine("GC. Second Execution.");

        GC.Collect();
        Console.WriteLine("GC. Third Execution.");

When debugging this code with SOS I see that after First Execution of GC addresses are following:

!dso
...
0239b5f8 System.Threading.SynchronizationContext
0239b5a8 System.Collections.Stack
0239b560 System.Text.StringBuilder
...

After Second Execution there is no 'stack' object in the heap, but other addresses are:

!dso
...
0239b5f8 System.Threading.SynchronizationContext
0239b560 System.Text.StringBuilder
...

So the 'stack' object was collected, but sc (SynchronizationContext) object was not relocated in the memory to be compacted. We have a gap in the memory

!do 0239b5a8
Free Object
Size:        80(0x50) bytes

After Third Execution situation is the same.

Why does it happen? Why 'compact' operation is not performed in this case?

Thank you.

  • Interesting. I'd guess that the synchronization context contains a bunch of static non-relocatable fields related to a singleton pattern. It's probably a case of requiring direct memory access to the object from another thread. – Polynomial Feb 07 '12 at 09:29
  • Thank you. Do you mean 'pinned' object? – user1194191 Feb 07 '12 at 09:41
  • I've tried to use custom Foo object against SynchronizationContext - the sutionations is the same. Object Foo is not replaced in the memory to male heap compacted. – user1194191 Feb 07 '12 at 09:43
  • The bigger question is why *all* objects didn't get collected. Don't test code that you compiled in the Debug configuration, you won't get representative results. – Hans Passant Feb 07 '12 at 11:29
  • Thank you, guys. I found the answer in the official Microsoft documentation (sorry for no link, it is printed documentation). After marking objects as unused (candidates to be deleted) GC executes Plan phase. "Plan: The .NET garbage collector creates a generation budget for each generation being collected and then determines the amount of fragmentation that will occur in the managed heap as a result of the GC to decide whether compaction will be productive." But I always thought that compaction was always done by GC. There was a gap in my knowledge, not in GC Heap ))) – user1194191 Feb 07 '12 at 12:01

1 Answers1

3

The GC is lazy to be efficient. It will not actually free or as you say 'compact' until it needs to. The object has been moved to a dispose queue.

You can wait days and not see it clean up. It will clean up when resources are low and it needs to.

Rob Smyth
  • 1,768
  • 11
  • 19
  • Rob, thank you. But 'stack' object is Free (cleaned up) as we see. The question is, why 'sc' object didn't take his adress 0239b5a8 – user1194191 Feb 07 '12 at 09:32
  • You have set the 'stack' object to be the 'null' object. It is not actually a null but is now a reference to the null object. So the identifier 'stack' now references a system object called null. – Rob Smyth Feb 09 '12 at 09:46