[ACCEPTED]-Circular References Cause Memory Leak?-circular-dependency

Accepted answer
Score: 45

Great question!

No, Both forms will be 23 (can be) GC'd because the GC does not directly 22 look for references in other references. It 21 only looks for what are called "Root" references 20 ... This includes reference variables on 19 the stack, (Variable is on the stack, actual 18 object is of course on the heap), references 17 variables in CPU registers, and reference 16 variables that are static fields in classes...

All 15 other reference variables are only accessed 14 (and GC'd) if they are referenced in a property 13 of one of the "root" reference objects found 12 by the above process... (or in an object 11 referenced by a reference in a root object, etc...)

So 10 only if one of the forms is referenced somewhere 9 else in a "root" reference - Then both forms 8 will be safe from the GC.

only way I can 7 think of to "prove" it, (without using memory 6 trace utilities) would be to create couple 5 hundred thousand of these forms, in a loop 4 inside a method, then, while in the method, look 3 at the app's memory footprint, then exit 2 from the method, call the GC, and look at 1 the footprint again.

Score: 15

As others have already said, GC has no problems 7 with circular references. I'd just like 6 to add, that a common place to leak memory 5 in .NET are event handlers. If one of your 4 forms has an attached event handler to another 3 object which is "alive", then there is a 2 reference to your form and the form will 1 not get GC'd.

Score: 12

Garbage collection works by tracking application 20 roots. Application roots are storage locations 19 that contain references to objects on the 18 managed heap (or to null). In .NET, roots 17 are

  1. References to global objects
  2. References to static objects
  3. References to static fields
  4. References on the stack to local objects
  5. References on the stack to object parameters passed to methods
  6. References to objects waiting to be finalized
  7. References in CPU registers to objects on the managed heap

The list of active roots is maintained 16 by the CLR. The garbage collector works 15 by looking at the objects on the managed 14 heap and seeing which are still accessible 13 by the application, that is, accessible 12 via an application root. Such an object 11 is considered to be rooted.

Now suppose 10 that you have a parent form that contains 9 references to child forms and these child 8 forms contain references to the parent form. Further, suppose 7 that the application no longer contains 6 a references to the parent for or any of 5 the child forms. Then, for the purposes 4 of the garbage collector, these managed 3 objects are no longer rooted and will be 2 garbage collected the next time a garbage 1 collection occurs.

Score: 5

If both the parent and child are not referenced, but 4 they only reference eachother, they do get 3 GCed.

Get a memory profiler to really check 2 your application and answer all your questions. I 1 can recommend http://memprofiler.com/

Score: 2

I'd like to echo Vilx's remark about events, and 18 to recommend a design pattern that helps 17 address it.

Let's say that you have a type 16 that is an event source, e.g.:

interface IEventSource
{
    event EventHandler SomethingHappened;
}

Here is a 15 snippet of a class that handles events from 14 instances of that type. The idea is that 13 whenever you assign a new instance to the 12 property, you first unsubscribe from any 11 previous assignment, then subscribe to the 10 new instance. The null checks ensure correct 9 boundary behaviors, and more to the point, simplify 8 disposal: all you do is null the property.

Which 7 brings up the point of disposal. Any class 6 that subscribes to events should implement 5 the IDisposable interface because events 4 are managed resources. (N.B. I skipped a 3 proper implementation of the Dispose pattern 2 in the example for brevity's sake, but you 1 get the idea.)

class MyClass : IDisposable
{
    IEventSource m_EventSource;
    public IEventSource EventSource
    {
        get { return m_EventSource; }
        set
        {
            if( null != m_EventSource )
            {
                m_EventSource -= HandleSomethingHappened;
            }
            m_EventSource = value;
            if( null != m_EventSource )
            {
                m_EventSource += HandleSomethingHappened;
            }
        }
    }

    public Dispose()
    {
        EventSource = null;
    }

    // ...
}
Score: 0

The GC can deal correctly with circular 35 references and if these references were 34 the only things keeping the form alive then 33 they would be collected.
I have had lots 32 of trouble with .net not reclaiming memory 31 from forms. In 1.1 there were some bugs 30 aroung menuitem's (I think) which meant 29 that they didn't get disposed and could 28 leak memory. In this case, adding an explicit 27 call to dispose and clearing the member 26 variable in the form's Dispose method sorted 25 the problem. We found that this also helped 24 reclaim memory for some of the other control 23 types.
I also spent a long time with CLR 22 profiler looking at why forms were not being 21 collected. As far as I could tell, references 20 were being kept by the framework. One per 19 form type. So if you create 100 instances 18 of Form1, then close them all, only 99 would 17 be reclaimed properly. I didn't find any 16 way to cure this.
Our application has since 15 moved to .net 2 and this seems to be much 14 better. Our application memory still increases 13 when we open the first form and doesn't 12 go back down when it is closed but I believe 11 this is becuase of JIT'ed code and extra 10 control libraries that are loaded.
I have 9 also found that although the GC can deal 8 with circular references, it seems to have 7 problems (sometimes) with circular event 6 handler references. IE object1 references 5 object2 and object1 has a method that handles 4 and event from object2. I found circumstances 3 where this didn't release the objects when 2 I expected but I was never able to re-produce 1 it in a test case.

More Related questions