Hi,
Very recently my application that uses an UltraTree started consuming vast resources. I have found that a single node object when part of a heirarchy will consume upwards of 50KB.
Furthermore if I add the root node object to a tree control's nodes collection, clearing the tree's node collection (using nodes.clear() ) does not release the references to the nodes. This means if you build a tree, then clear it's nodes and try to rebuild it, you will be holding two full trees in memory. At upwards of 50KB per node, this will bring a system to it's knees when you are working with trees containing thousands of nodes.
The same project used to consume far less resources prior to upgrading to 2011.1. Can you please advise?
I have attached a simple project that procedurally generates a tree as an example (using 2011.2 where the problem still exists).
Thanks.
I ran your sample, but there doesn't appear to be any code in it for tracking the amount of memory usage. How are you determining the memory usage? Are you using a memory profiler? Or are you relying on the Windows Task Manager.
I would strongly advise against using the TaskManager, as it will not give you an accurate portrayal of the memory usage for a DotNet application, since it does not account for Garbage collection.
There is also nothing in your sample that disposes of the nodes or the tree control, so I don't see how this sample could demonstrate that the the nodes.Clear method is not releasing the resources.
And since your sample generates a random number of nodes each time, it's impossible to make any comparison between one version and the next.
So on what are you basing the claim that the tree uses more memory in the latest version?
Hi Mike,
I found a development server running VS2008 and NetAdvantage v9.1. I created the same project and found that the tree uses only ~100MB when loaded once (compared to the ~500MB). Rebuilding the tree will start releasing memory after the second click, and the overall memory usage of the application doesn't exceed ~200MB.
I have attached the application here.
I can rewrite the application to generate a fixed number of nodes, but you can see the number doesn't vary from ~10500 nodes too much, and comparing these two applications should be enough.
Let me know if you need anything else.
Thanks
Comparing these two applications is not a valid comparison for a number of reasons.
For one thing, the randomness is going to skew the results. But more importantly, the code in these samples is completely different. In the first sample, you are not even adding the nodes into the tree, you are just creating nodes in code and letting them go out of scope. In the second sample, you add these nodes into the tree and then clear the tree, but not until after you create a whole new set of nodes that are not in the tree.
So this whole sample application is going to be very confusing and difficult to work with in terms of finding any kind of memory issues.
Also, you seem to be under some misconceptions about how memory management and the GarbageCollector work.If your application is ultimately crashing because of an OutOfMemory exception, then something is probably using more and more memory.
But you cannot assume that the GarbageCollector is being called to collect unused memory every time your application requests more - that's not how it works. When the GC is called is completely arbitrary and you cannot assume when it will occur unless you explicitly invoke it yourself.
If you create a bunch of tree nodes, as in your first sample, and then you create a bunch more and the memory usage of your application goes up (the first set of nodes are not released), then that does not necessarily indicate a problem. You cannot just assume that the GC was (or should have been) called at that point. The proper way to do a test like this would be to make sure that the GC is called. Something like this will do it:
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
GC.Collect();
GC.WaitForPendingFinalizers();
As a general rule, if you create an object, you are responsible for disposing for that object. If you create a bunch of nodes and those nodes are never added to a Tree, then there's no way those nodes can possibly know when they should be disposed. So it would not be surprising if the first sample you posted here had a memory leak - although I am not sure that is, in fact, the case.
My understanding is that the GC should be called whenever an application asks for more memory than is apparently available, so it should avoid OutOfMemoryExceptions whenever possible.
In your second sample, where you are adding the nodes to the tree, it's something of a gray area whether or not the tree should dispose those nodes when they are removed from the tree. After all - you could very well be holding a reference to those nodes in a variable. So if removing them from the tree disposes them and you try to access those nodes you are still holding on to, your application would crash with an exception.
I took a quick look at the tree's source code and it does not look like the tree is actually disposing of the nodes that are removed, but I also don't see anything that should be holding on to the node once it is removed from the tree (or if it was never added to a tree).
Anyway... I tried running your new sample through Ants. I took an initial memory snapshot and then clicked the button and took another one. The memory usage (with v9.1) goes up by about 11MB. Clicking the button again increased the memory usage by about another 2MB. After that, the memory usage stays about the same.The biggest increase here seems to be from the UnmanagedMemoryStream class. I suspect this has something to do with the way you are retrieving your image resources.
I then tried the same experiment with v 11.2 of the tree.The first time I click the button, the memory usage goes up by about 12MB. On the second and subsequent clicks, the memory usage does not change significantly. If anything, it's going up even less than in the v9.1 version.
BTW... while the disposal of the nodes is something of a gray area, the disposal of the images is not. Your code creates these images and you are responsible for disposing them when they are no longer needed. There is no way the tree can make the assumption that just because the node is no longer needed that the image is no longer needed, either. The tree cannot dispose these images for you, you need to do it.
So I'm not seeing anything here that looks like a memory leak or any increase in the amount of memory used by the tree nodes.
Since the platform the application is running on was different for the different versions, I decided we need to eliminate that variable as well. I copied the v9.1 assemblies to the x64 server I built the VS2010 version on, and ran the VS2008 version there.
To my surprise the same 2008 version that only uses 98MB on Server 2003 (32-bit) uses nearly 700MB on Server 2008 r2 (64-bit)! I would expect a small increase in the amount of memory used when moving from 32 to 64-bit, but a near 7x increase?
Is this to be expected? How should we proceed?
If I understand you correctly, it seems like the problem all along has not had anything to do with the new versions of the controls, but rather was a function of the operating system. Is that right?Are you essentially getting the same results from v9.1 and v11.2 if you run them on the same machine?
I took a look at your new sample and this seems like it will be a much better test of memory usage. Personally, I wouldn't use randomly-generated data and random images, but it's probably not a big deal.
Anyway, I'm running Windows 7 64-bit and I ran your sample through Ants. I started profiling and when the form comes up, I take a snapshot.
Total size of objects in snapshot 1: 375.1KB
I then push the button to populate the tree and I get this:
Total size of objects in baseline snapshot snapshot 1: 375.1KB
Total size of objects in snapshot 2: 17.69 MB
Difference between snaphot 2 and snapshot 1: +17.33 MB
So in this new sample, it's a little more memory than in the previous one, but still only 17 MB.
I really have no idea why the same sample application would be using 700 MB, or even 98 MB on your machine.
But I can tell you that there is nothing specific to the controls that does anything any differently on a 32-bit machine vs. a 64-bit machine. So if the memory usage is different and you are certain that you are using exactly the same sample application, the same version of the Infragistics assemblies, and the same version of the DotNet framework, then the only explanation I can think of is that it's the O/S.
BTW, I'm using Ants Memory Profiler 7.0.0.731, which I think is the latest version (or close to it). Perhaps there is something else going on in the version of Ants you have. If you want to post the results of your Ants profiling here, I will be happy to take a look at it, but I'm not sure what good that will do.
Can I ask what OS you are using for your tests? I am trying to figure this out and hopefully find a remedy. Part of this is running on a variety of operating systems and comparing the memory usage. It would be good to know what you are using for a comparison.
When I have results from all my available operating systems, I'll post back.
Y'know... I just took a look at your last sample and I noticed that you are getting the image from the resource every time, so now that I think about it, I'm pretty sure that this is not re-using the same image instance. This looks like it is creating a new Image object every time you call into your GetRandomSourceImage method.
You should probably get each resource once and cache it (in a Dictionary, perhaps) so you can re-use the same image. That should save you a huge amount of memory.
Since you are re-using the same small set of images, I can't see any reason why the images should be making any significant difference here.
Clearly there must be something different between my machine and your Windows 7 64-bit machine, but what it might be is a mystery.
I doubt it's the version of the control. v11.2 was only recently release, so there's very little difference between the internal version I am using and the one you are using and there have been no changes to the tree that I can see.
Also, since the memory usage on my machine does not change much from 9.1 to 11.2, it seems unlikely that there has been any change in the control to account for the difference.
So that pretty much just leaves the version of Visual Studio and the DotNet Framework. Your sample is in Vs2008 and my version of that is
9.0.30729.1 SP
And the version of the DotNet Framework I am using for your sample is 3.5 SP1.
So maybe check those versions on your machine(s) and see if there is any consistency there.
At first it seemed this was a 32-bit vs 64-bit OS issue. Most but not all the 64-bit platforms tested had ~700MB usage when the tree was loaded once. All the 32-bit platforms tested only use ~100MB when the tree was loaded once. I have confirmed it has nothing to do with v9.1 vs v11.2. The newer version does indeed use less memory by comparison in all circumstances.
Windows XP Pro SP2 (32-bit): ~100MB
Windows 7 Pro SP1 (64-bit): ~700MB
Windows Server 2008 SP2 (64-bit): ~100MB
Windows Server 2008 R2 SP1 (64-bit): ~700MB
I have been frantically trying to figure out what makes the Server 2008 SP2 machine stand out. I'm not sure if it could have to do with installed dotNET versions / service packs, enabled windows features, or what. I haven't had any luck yet.
I don't want to have to resort to dropping the Infragistics tree control from my application, but if I can't find a solution soon, I will have to. Any advice on how to hunt this issue down? ANTS isn't any help because the memory usage is all unmanaged.
I have confirmed that the issue is all about the node images. If I don't set the node LeftImages, the application uses very little memory on all platforms (~25MB). Could this be an issue with GDI?
Oh sorry, I see you stated you are using Windows 7 64-bit. I don't understand this at all because when I run the example app on my workstation (Windows 7 Professional 64-bit) the memory usage is over 600 megs when loading the tree once.
Can you appease me and run this release mode application using the contained assemblies, and verify the memory usage does not change?
Oh well unfortunately I cannot upload a file larger than 200k. Is there some other way I can get this deployment to you for testing?