Situation
According to this accepted answer, if the "GC 'sees' a cyclic reference of 2 or more objects which are not referenced by any other objects or permanent GC handles, those objects will be collected."
I wanted to know if garbage collection works for a super simple tree structure that doesn't even have content, just tree-nodes with parent- & child-references.
Imagine you create a root-node add a child to it and then a child to the child and so on, so not really a tree but more like a list (each node has at most one child and one parent).
If we then remove the root's child and all references to node's within this child's subtree as I understand the answer above, the garbage collector should clean up the subtree.
Problem Description
If you have a look at the Main-method in the test-code below, when running the exe from the Release-directory I get the behavior I would expect memory consumption increases upto ~1GB then goes down to ~27MB (after the 1. GC.collect) up again and then down to ~27MB again (for the 2. GC.collect).
Now when running it in the debugger memory consumption for doing this goes up to ~1GB and for the 1.GC.collect memory consumption stays exactly where it was then goes up to 1.6GB withing the 2nd for-loop takes ages there and then I finally get an OutOfMemoryException within the 2nd for-loop.
Questions
Why do I get this behavior in the debugger?
Shouldn't garbage collection work during debugging as well, am I missing some Info about the debugger?
Side notes
- I'm using visual studio 2010 Express edition
- I only call GC.Collect() for the specific testing purposes here to be sure that garbage collection should have taken place.(I don't plan to use it normally)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Tree
{
class Program
{
static void Main(string[] args)
{
TreeNode root = new TreeNode(null); // the null-argument is the parent-node
TreeNode node = root;
for (int i = 0; i < 15000000; i++)
{
TreeNode child = new TreeNode(node);
node = child;
}
root.RemoveChild(root.Children[0] );
node = root;
GC.Collect();
for (int i = 0; i < 15000000; i++)
{
TreeNode child = new TreeNode(node);
node = child;
}
root.RemoveChild(root.Children[0]);
node = root;
GC.Collect();
Console.ReadLine();
}
}
}
I only include the following code in case you want to test it for yourself, it's not really useful
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Tree
{
class TreeNode
{
public TreeNode Parent { get; private set; }
public List<TreeNode> Children { get; set; }
public TreeNode(TreeNode parent)
{
// since we are creating a new node we need to create its List of children
Children = new List<TreeNode>();
Parent = parent;
if(parent != null) // the root node doesn't have a parent-node
parent.AddChild(this);
}
public TreeNode(TreeNode parent, List<TreeNode> children)
{
// since we are creating a new node we need to create its List of children
Children = new List<TreeNode>();
Parent = parent;
if (parent != null) // the root node doesn't have a parent-node
parent.AddChild(this);
Children = children;
}
public void AddChild(TreeNode child)
{
Children.Add(child);
}
public void RemoveChild(TreeNode child)
{
Children.Remove(child);
}
}
}