5

I have read this article: http://blogs.msdn.com/b/oldnewthing/archive/2010/08/10/10048149.aspx and honestly I don't understand every detail. As far as i understand in the code below c should be collected even if i don't set c to null. Another thing is that allocations happening during a foreach seem to be not freed as long as we in the scope of the same function. (See example below)

class Program
{
    public class SomeClass
    {
        public byte[] X;

        public SomeClass()
        {
            X = new byte[1024 * 1024 * 100];
            X[155] = 10;
        }
    }

    static void Main()
    {
        Console.WriteLine("Memory: " + GC.GetTotalMemory(false));

        SomeClass c;
        c = new SomeClass();

        Console.WriteLine("Memory: " + GC.GetTotalMemory(false));
        GC.Collect();
        Console.WriteLine("Memory: " + GC.GetTotalMemory(true));
        Console.ReadKey();

        /*
         * Output:
         * 
         * Memory: 186836
         * Memory: 105044468
         * Memory: 104963676
         * 
         */
    }
}

EDIT The solution for the first example: The Debug-Mode (not running in debugger but even the compilation-mode). If I use Release it'll work as expected: c is collected even without setting to null. For Second example the same applies.

Second example

static void Main(string[] args)
{
    Console.WriteLine("Startup Memory: " + GC.GetTotalMemory(false));

    var obj = BOObject.Get();

    Console.WriteLine("Fetched Object: " + GC.GetTotalMemory(false));
    GC.Collect();
    Console.WriteLine("Fetched Object (Collected): " + GC.GetTotalMemory(false));

    foreach (var node in obj.Traverse())
    {
        string name = node.Name;
    }

    Console.WriteLine("Traversed: " + GC.GetTotalMemory(false));
    GC.Collect();
    Console.WriteLine("Traversed (collected): " + GC.GetTotalMemory(false));

    obj = null;

    GC.Collect();
    Console.WriteLine("collected: " + GC.GetTotalMemory(true));
    Console.Read();
}

Output:

Startup Memory: 193060 Fetched: 8972464 Fetched (Collected): 5594308 Traversed: 272553096 Traversed (collected): 269564660 collected: 269564048

If i put the foreach loop in another function and call this function the memory used after .Collect is call is about 5800000. So why the garbage is not collected when i have the foreach loop in the same function?

Daniel Bişar
  • 2,663
  • 7
  • 32
  • 54

2 Answers2

7

Mentioned in the comments to Raymond's article and in Yun Jin's MSDN article here that

In fact, for debuggable code, JIT extends lifetime for every variable to end of the function.

Therefore your collection won't be collected in Debug mode as you discovered and why it will be collected in Release mode.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
MerickOWA
  • 7,453
  • 1
  • 35
  • 56
0

The garbace collector's behaviour is not deterministic and should not be expected to be deterministic. Don't rely on the GC.

You are asking why the GC is not collecting your garbage at a given point in time. The better question would be why it is collection at a given point in time.

PVitt
  • 11,500
  • 5
  • 51
  • 85
  • http://msdn.microsoft.com/en-us/library/xe0c2357.aspx It is not deterministic but you can force a collection which will try to collect not used objects. GC.GetTotalMemory(true) waits for the current collection to finish. So at least some data should be freed. That it will not be the same amount each try is ok but the difference of 200 MB is not expected... – Daniel Bişar Jun 21 '11 at 12:27
  • 1
    @Malone it is not deterministic. So the phrase "So at least some data should be freed" doesn't apply. In order to be able to say that it would have to be deterministic. – Rangoric Jun 21 '11 at 12:54