Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does GC work with IEnumerator and yield?

I understand that enumerators and the yield keyword can be used to help with async/staggered operations, as you can call MoveNext() to run the next block of code.

However, I don't really understand what that Enumerator object is. Where does the memory in use of the scope of the Enumerator go? If you don't MoveNext() an Enumerator all the way, does it get GC'd eventually?

Basically, I'm trying to keep my GC hits down, as I am potentially using a LOT of Enumerators and GC can be an issue inside Unity, especially due to the older version of Mono it uses.

I have tried to profile this but can't wrap my head around them still. I don't understand the scoping/referencing that happens with Enumerators. I also don't understand if Enumerators are created as Objects when you create one from a function that yields.

The following example shows my confusion better:

// Example enumerator
IEnumerator<bool> ExampleFunction()
{
    SomeClass heavyObject = new SomeClass();
    while(heavyObject.Process())
    {
        yield return true;
    }

    if(!heavyObject.Success)
    {
        yield return false;
    }

    // In this example, we'll never get here - what happens to the incomplete Enumerator
    // When does heavyObject get GC'd?
    heavyObject.DoSomeMoreStuff();
}

// example call - Where does this enumerator come from? 
// Is something creating it with the new keyword in the background?
IEnumerator<bool> enumerator = ExampleFunction();
while(enumerator.MoveNext())
{
    if(!enumerator.Current)
    {
        break;
    }
}

// if enumerator is never used after this, does it get destroyed when the scope ends, or is it GC'd at a later date?
like image 368
mGuv Avatar asked Feb 12 '16 10:02

mGuv


2 Answers

You probably should read an enumerator internals post. From the internals you can answer all these questions.

A crash course: Each iterator method execution returns a new enumerator object. Local variables become fields.

If nobody uses that enumerator object anymore it is eligible for collection. Also, all references that local variables were creating go away for GC purposes.

There are some edge cases for when exactly local variables stop being GC references. If your enumerators are fairly short lived this does not matter much.

The CLR does not know what an enumerator is. It's just a class generated by the C# compiler.

Pulling the link by xanatos into this answer since it's illustrative and he does not seem to post an answer: http://goo.gl/fs4eNo

like image 69
usr Avatar answered Oct 12 '22 20:10

usr


// if enumerator is never used after this, does it get destroyed when the scope ends

Correct

Eligibility for collecting is having no references. For GC's scoping there is nothing special with enumerators. Enumerators disposed/cleared in the same way when they go out of scope as any other type.

like image 28
CharithJ Avatar answered Oct 12 '22 20:10

CharithJ