views:

182

answers:

2

or, in other words:

Can an object referenced by a local variable be reclaimed before the variable goes out of scope (eg. because the variable is assigned, but then not used again), or is that object guaranteed to be ineligible for garbage collection until the variable goes out of scope?

Let me explain:


void Case_1()
{
    var weakRef = new WeakReference(new object());

    GC.Collect();  // <-- doesn't have to be an explicit call; just assume that
                   //     garbage collection would occur at this point.

    if (weakRef.IsAlive) ...
}

In this code example, I obviously have to plan for the possibility that the new'ed object is reclaimed by the garbage collector; therefore the if statement.

(Note that I'm using weakRef for the sole purpose of checking if the new'ed object is still around.)


void Case_2()
{
    var unusedLocalVar = new object();
    var weakRef = new WeakReference(unusedLocalVar);

    GC.Collect();  // <-- doesn't have to be an explicit call; just assume that
                   //     garbage collection would occur at this point.

    Debug.Assert(weakRef.IsAlive);
}

The main change in this code example from the previous one is that the new'ed object is strongly referenced by a local variable (unusedLocalVar). However, this variable is never used again after the weak reference (weakRef) has been created.


Question: Is a conforming C# compiler allowed to optimize the first two lines of Case_2 into those of Case_1 if it sees that unusedLocalVar is only used in one place, namely as an argument to the WeakReference constructor? i.e. is there any possibility that the assertion in Case_2 could ever fail?

+6  A: 

It doesn't matter what the C# compiler does - the JITter/GC are allowed to clean up local references once they're no longer alive in a method body. Look at the docs for GC.KeepAlive

Also, this powerpoint presentation, especially from slide 30 onwards, helps to explain what the JIT/GC can get up to.

Damien_The_Unbeliever
Also note that, in debug builds, the variable is explicitly kept alive to the end of the scope for the debugger to view -- it's only in release builds you'll see this behaviour.
Andy Mortimer
@Andy - interesting point. Not that it matters, but I'm guessing this behaviour is governed by the JITter?
Damien_The_Unbeliever
And for completeness, that is why setting `unusedLocalVar=null` at the _end_ of the method is usually a de-optimization.
Henk Holterman
*@Damien_The_Unbeliever:* Thanks for the `GC.KeepAlive` cue, I think the MSDN documentation gives a 95% answer to my question. -- *@Henk Holtermann:* This seems less safe than `GC.KeepAlive`; couldn't a smart compiler recognise such a `null` assignment as superfluous and optimise it away, too?
stakx
Yes, the JIT optimizer will remove the null assignments. The garbage collector gets cues for the lifespan of a local variable from the JIT compiler. GC.KeepAlive is a manual version of such a cue, it doesn't actually generate any code.
Hans Passant
A: 

Good question: I think the best answer would be "it depends". If the reference was inlined, and if the GC ran and memory was in demand, then perhaps: this will depend on the implementation.

My opinion: since this can only come about with a WeakReference, you're explicitly acknowledging the possibility that your reference may be collected at any time and code accordingly. It may not happen now, but if the VM configuration is changed or the VM optimiser changes in a proceeding release it may start to fail.

@Damien_The_Unbeliever: unusedLocalVar is in scope and thus not available for collection until the end of the method body in this example, unless it is inlined, in which case it may be collected beforehand.

JT
unusedLocalVar is dead after the last statement that references. Hence my link to GC.KeepAlive (and it's reason for existing). The JITter/GC have been designed to be aggressive about what they can reclaim.
Damien_The_Unbeliever
@JT: You refer to inlining. Who or what does the inlining? AFAIK you can't control inlining with any C# language feature, right? -- Note also that the weak reference object is of no importance. I only use it as a means to figure out what the garbage collector might be doing. The real question here is: Can an object referenced by a local variable be reclaimed before the variable goes out of scope (eg. because the variable is assigned, but then not used again), or is that object guaranteed to be ineligible for GC until the variable goes out of scope?
stakx
@Damien_the_Unbeliever: thanks for pointing that out, I've learnt something.@stakx: I was referring to JIT inlining, though the compiler may also optimise. My point was that it's not known if this will occur. Whether it does depends on what optimisations the current JIT supports, how aggressive it is, and how much memory pressure it's under. I thought that objects did stay valid for their scope; as Damien_the_Unbeliever points out, they may not.
JT