runtime: Why GC doesn't collect the object?

I’m facing a confused GC collection behavior, the following code is to test whether GC collect LOH object “as expected”. I build the code under x86 + Debug, as I want to use the 2G memory limitation for test, I try allocating a 850M byte array in a while loop, the ‘850’ comes from my test, which I suppose I can allocate one such array, but I cannot allocate another one, so that I can realize a out of memory error if GC doesn’t free the previous one. You can tweak this value on your machine.

My test machine installed x64 OS and has more than 2G available memory.

.netcore 3.1 console app (x86 + Debug)

    // Out of memory, why?
    while (true)
    {
        _ = new byte[850 * 1024 * 1024];
        Console.WriteLine(GC.GetTotalMemory(true) / 1024f / 1024);
    }

    // Out of memory, why?
    while (true)
    {
        var a = new byte[850 * 1024 * 1024];
        Console.WriteLine(GC.GetTotalMemory(true) / 1024f / 1024);
    }

    // Out of memory, why?
    while (true)
    {
        var a = new byte[850 * 1024 * 1024];
        a = null;
        Console.WriteLine(GC.GetTotalMemory(true) / 1024f / 1024);
    }

Same tests on .NET Framework (4.6.1 and 4.8)

    // No out of memory, as expected!
    while (true)
    {
        _ = new byte[850 * 1024 * 1024];
        Console.WriteLine(GC.GetTotalMemory(true) / 1024f / 1024);
    }

    // Out of memory, why?
    while (true)
    {
        var a = new byte[850 * 1024 * 1024];
        Console.WriteLine(GC.GetTotalMemory(true) / 1024f / 1024);
    }

    // No out of memory, as expected!
    while (true)
    {
        var a = new byte[850 * 1024 * 1024];
        a = null;
        Console.WriteLine(GC.GetTotalMemory(true) / 1024f / 1024);
    }

And on .NET Framework, if I put the code in try block, then all of them out of memory.

try{
    // case1
    // Out of memory, why?
    while (true)
    {
        _ = new byte[850 * 1024 * 1024];
        Console.WriteLine(GC.GetTotalMemory(true) / 1024f / 1024);
    } 
    // case2
    // Out of memory, why?
    while (true)
    {
        var a = new byte[850 * 1024 * 1024];
        Console.WriteLine(GC.GetTotalMemory(true) / 1024f / 1024);
    }
    // case3
    // Out of memory, why?
    while (true)
    {
        var a = new byte[850 * 1024 * 1024];
        a = null;
        Console.WriteLine(GC.GetTotalMemory(true) / 1024f / 1024);
    }
} catch { ... }

But if I move code into methods, the results different again.

try { 
Case1();   // No out of memory now.
or
Case2();  // Still out of memory.
or
Case3();  // No out of memory now.
} catch { ... }

I didn’t find any doc on these behavior, so I posted the questions here.

Thank you.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 21 (21 by maintainers)

Most upvoted comments

Tested some of them in Release, still not works as expected. I suppose all of these can work without out of memory, or at least except case2, all others should work.