Hi,
I'm using Infragistics4 v12.1 with my winform application and decided to build dynamically the TreeNodeCollection attached to my UltraTree.This TreeNodeCollection contains one root node and multiple child nodes on potentially a lot of levels (depth).
I figured it would be more efficient to create this TreeNodeCollection instead of binding a collection.In the class that builds the TreeNodes hierarchy, I also set some Appearances on the cells based on their values.
I thought it would be a good idea to keep these Appearance instances as static references of that building class since they would be used multiple times (thousands).
By doing so, I realized that the TreeViewNodes are not Garbage Collected, even if they were disposed manually. So I'm back to cloning as many instances of the Appearance instances as the number of cells that should display a green background for example.
Is there a better way to do it ? Is this memory leak normal ?
If you dispose the node and it's still not getting garbage collected, then that's a bug. Can you post a small sample project demonstrating this behavior?
Hi Mike,
Thanks for your answer. I've attached here the project I'm using to investigate this particular case.
You will see in the TreeNodeBuilder class the two ways to associate Appearance (the leaky one and the non-leaky one).
Thanks,
I ran your sample through the Ants Memory Profiler and I don't see any memory leaks here.
I made one small change to your sample - I added some code to force a garbage collect after clearing the nodes.
What I did was I ran your sample and clicked the UltraTree button to show the tree form. Then I added nodes to the tree and clicked ExpandAll. At this point, I took a memory snapshot.
Then I cleared the nodes and took another snapshot and compared the two and there were a few minor increases in the number of instances of certain DotNet Framework classes. But nothing significant or related to the tree nodes or the appearances.
So what makes you think there's a memory leak here? What classes are not properly being disposed or GC'd?
When the Appearance is static and you successively click the "Add Nodes" / "Clear Nodes" buttons multiple times, you will notice that the memory is rising very quickly and leads to a OutOfMemoryException in the most extreme cases.
On the other hand, by commenting that line and uncommenting the one that does a Clone() on the static Appearance before setting it to the Cell, the same operation leads to a stable memory.
I'm not definitive on the conclusions, maybe I'm doing something wrong but this seems a bit odd, no ?
We've been looking into this issue in depth and it's a bit more complex than it seems. So far, I haven't been able to find a way to fix this, but I think I have a workaround for you that might be acceptable.
What's happening here is that when you assign a Appearance to the cell, the cell has to hook into the property change notification on the Appearance object. This is so that if you change something on the appearance, the cell knows about it and refreshes it's display.
You might expect that once you clear the nodes in the tree, that the nodes and the cells are disposed and so the cell would unhook from this notification. But that's not how it works. The tree was designed in such a way that the nodes are independent of the tree control. You can create an UltraTreeNode in code and hold a reference to it, add it to a tree, remove it from that tree, add it to a different tree, etc. This gives you some flexibility in how nodes are handled, but it means that the tree control cannot assume that just because you are removing a node from the tree that that node is no longer being referenced and can therefore be disposed.
So the tree does not dispose the nodes. Since you create the nodes, you are responsible for disposing them. This is why this works in the grid. In the grid, you don't create the rows - the grid creates them and therefore the grid can dispose them when they are no longer needed.
There are a number of ways we could fix this in the tree. We could, for example, add an overload to the Clear method that takes a boolean that indicates whether to dispose the nodes. The problem with this is that it's not very performant. It would force the tree to loop through every node and every cell when clearing and that would take a lot more time than the current implementation of the Clear method.
I think the best and easiest solution here would to make a change in your code. There's no reason for you to clone the appearance for each individual cell. All you really need to do is keep the appearance in synch with the entire tree. That is.. create a clone once when you populate the tree and destroy that clone when you clear the tree.
I have attached a modified version of your sample here which demonstrates this technique. All of the changes I made to the sample are commented using my initials (MRS) so you can easily see what changed. Please let me know if you have any questions.
Thank you Mike for this detailed answer.I'm currently away for a few days but will check this solution once I'm back.