For instance if I have a hierarchical data structure:
class Node
{
public List<Node> children;
}
and it is populated to many levels down then in one of the parents go:
myNode.children.Clear();
which will clear all the references to the immediate children - but how about all the grand children, grand grand children etc. that were referenced by those immediate children? Is C# clever enough to know they are no longer needed and they will be garbage collected?
I have read using WPF data binding without implementing interface INotifyChanged can cause memory leaks: http://blogs.msdn.com/b/micmcd/archive/2008/03/07/avoiding-a-wpf-memory-leak-with-databinding-black-magic.aspx, how is that possible in a managed environment?
C# does not care. The it's the CLRs job to do the GC.
The GC starts at known root objects(static fields, local variables,...) and walks the references until it has found all reachable objects. All other objects can be collected(excluding some finalizer related stuff).
So if the child references were really the only references to these objects then the grand children will be collected too. But if some alive outside object still has a reference to one of your nodes this node and all other objects referenced by it will be kept alive.
Managed memory leaks are caused by references which keep objects alive.
For example when using databining the GUI has references to the objects keeping them alive.
Similarly being subscribed to an event keeps the object associated with the event handler alive. So sometimes events use weak references to avoid this problem.
Circular references are no problem for the GC in .NET. It uses an algorithm to determine which objects are actually reachable from certain entry points (e.g. the main method).
What can cause meory leaks, however, are objects which are accidentally referenced by static members for example.
Your example falls into the first category and is therefore safe to use.
Yes, leaks in C# are caused when references to objects are not properly removed once those objects are no longer needed. If a reference to an object has been removed, then the object is got rid of by the Garbage Collector when it is run (it does this automatically based at times determined by a carefully tuned algorithm, so best not to manually cause it to run unless you really know what you’re doing!). But if the reference to the object isn’t properly removed, the Garbage Collector still thinks it is needed by the application, so the memory is leaked. It’s particularly common to find this sort of happening with event handlers which aren’t properly got rid of. If an object with children / grandchildren has all references to it removed, then that object as well as all those children / grandchildren will also be removed the next time the Garbage collector is run (unless they're also being referenced from elsewhere).
The best thing is to use a memory profiler, which will let you look at what objects are holding other objects in memory (most let you take snapshots of memory then look at some kind of graph showing the references. If an object still exists when it shouldn’t, you can look at a graph showing what reference is holding that object in memory, and use that to work out where you should have cleared the reference to avoid the memory leaking. There are a few profilers available but I find ants memory profiler by red gate the easiest to use http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/.
I'd suggest reading up on how garbage collection is handled in the .net world -- esentially, it works by following references to find anything that could be referenced by a top level object and frees everything else; it doesn't work with destructors like the C++ world, so you can be happy in the knowledge that managed objects will "just go" if their parent(s) and grand-parent(s) are freed.
Of course, the garbage collector only knows about managed memory, and it is worth looking at the IDisposable pattern if you have any unmanaged resources - this allows deterministic release of non-managed objects.
The complicated bit comes in when dealing with what could reference an object, and it does include some less obvious things, like event handlers, which is where the WPF/INotifyPropertyChanged issue you mentioned comes from.
It is possible to have a kind of memory leak in .NET.
If you have an object "A" that registers to an event on another object "B", then "B" gets an reference to "A" and will continue to have so if you do not unregister the event when "A" gets out of scope. In this case "A" cannot be garbage collected as there is still an active reference. It will stick around until "B" is garbage collected.
If you have a situation where "A" objects are created and goes out of scope continually you will get more and more "A"s in memory.