New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GC does not clean up memory allocated in same method #20156

Open
ayende opened this Issue Sep 27, 2018 · 3 comments

Comments

Projects
None yet
3 participants
@ayende

ayende commented Sep 27, 2018

This only happens in debug builds, when using release builds, the code below works properly.
During debug build, even though we have cleared the reference to b, the GC still maintains that it is tracked.
In fact, if we break into this using WinDBG, we can see that there is such a root holding the value, but I can't figure out where it is coming from.

I'm aware of the release / debug GC behaviors with respect to not eagerly clearing out variables that are still in scope, but in this case, we are explicitly setting the value to null and invoking the GC to clean things up.

using System;

namespace ConsoleApp15
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Console.WriteLine(GC.GetTotalMemory(true));
            WeakReference f = null;

            byte[] b;
            b = new byte[1024 * 1024 * 64];
            f = new WeakReference(b);
            b = null;

            for (var i = 0; i < 3; i++)
            {
                GC.WaitForPendingFinalizers();
                GC.Collect(2);
            }

            Console.WriteLine(GC.GetTotalMemory(true));
            Console.WriteLine(f.IsAlive);
        }
    }
}

This actually showed up for us when we noticed high memory utilization in a scenario where it shouldn't. This was using a debug build, and while we cleared up the field that was holding the value, we had the same experience. Under debug, there is something else that holds a reference to the value.

@jkotas

This comment has been minimized.

Show comment
Hide comment
@jkotas

jkotas Sep 27, 2018

Member

This is same issue as:

#15207
#5826 (comment)

The JIT can produce temporary local variables. These temporary local variables are outside your control and they may result into the reference lifetime getting extended. The likelihood of this happening is much higher with optimizations off. This issue can happen with optimizations on as well for more complex methods.

Member

jkotas commented Sep 27, 2018

This is same issue as:

#15207
#5826 (comment)

The JIT can produce temporary local variables. These temporary local variables are outside your control and they may result into the reference lifetime getting extended. The likelihood of this happening is much higher with optimizations off. This issue can happen with optimizations on as well for more complex methods.

@ayende

This comment has been minimized.

Show comment
Hide comment
@ayende

ayende Sep 27, 2018

A slightly adjusted version is here:
https://gist.github.com/ayende/3788b7cebc63c1cdd7ab614186e6ca78

This simulates the original issue we had, a long running loop that holds state and clears it.
But the value wasn't cleared and so the value we held in memory over what we expected.
That led to us holding an entire object graph.

It doesn't reproduce in release, but I'm worried if something like this can happen on release?

Also, WinDBG gives inconsistent results:
https://gist.github.com/ayende/535a857ffa75b06f1fa2f1642eaf6607#file-clrstack-txt-L16

https://gist.github.com/ayende/75fd760a83252730786b5feb8faf887a

I wonder if a good choice for long running loops would be to do the inner loop work in its own method, to avoid this ?

ayende commented Sep 27, 2018

A slightly adjusted version is here:
https://gist.github.com/ayende/3788b7cebc63c1cdd7ab614186e6ca78

This simulates the original issue we had, a long running loop that holds state and clears it.
But the value wasn't cleared and so the value we held in memory over what we expected.
That led to us holding an entire object graph.

It doesn't reproduce in release, but I'm worried if something like this can happen on release?

Also, WinDBG gives inconsistent results:
https://gist.github.com/ayende/535a857ffa75b06f1fa2f1642eaf6607#file-clrstack-txt-L16

https://gist.github.com/ayende/75fd760a83252730786b5feb8faf887a

I wonder if a good choice for long running loops would be to do the inner loop work in its own method, to avoid this ?

@janvorli

This comment has been minimized.

Show comment
Hide comment
@janvorli

janvorli Sep 27, 2018

Member

A separate method would fix that reliably only if it is marked as non-inlineable.

Member

janvorli commented Sep 27, 2018

A separate method would fix that reliably only if it is marked as non-inlineable.

grisha-kotler added a commit to grisha-kotler/ravendb that referenced this issue Sep 29, 2018

grisha-kotler added a commit to grisha-kotler/ravendb that referenced this issue Sep 29, 2018

ppekrol added a commit to ravendb/ravendb that referenced this issue Oct 2, 2018

ppekrol added a commit to ravendb/ravendb that referenced this issue Oct 2, 2018

myarichuk added a commit to myarichuk/ravendb that referenced this issue Oct 10, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment