When I finalized my article about memory leaks, I removed a part about Infragistics NetAdvantage. Here it is. It may be useful to some of you. Warning: It's based on NetAdvantage 7.3, and may or may not apply to recent versions.
Let's take an example. In project X, visual controls from the Infragistics NetAdvantage suite are used to build the GUI. One of these controls is the UltraToolbar. As told by its name, this control is used to display toolbars. The way a UltraToolbar is used is via a UltraToolbarsManager component. This works fine, except that even though the UltraToolbar class implements IDisposable, the UltraToolbarsManager class never calls the Dispose method on the UltraToolbars it manages. This is a bug. Fortunately, a workaround is easy to find: just call Dispose by yourself on each UltraToolbar. Unfortunately, this is not enough because UltraToolbar itself is buggy: it does not dispose the controls (buttons, labels, etc.) it contains. Again, the solution is to dispose each control the toolbar contain, but that's not so easy this time because each sub-control is different. Infragistics has the same problem with another set of objects: the custom editors you can create for cells in grids are not disposed automatically either.
Anyway, these are just specific examples. My point is that any libraries and components you use may cause leaks in your applications.
Later in the article:
Let's take an interesting example that shows another particularity of Infragistics NetAdvantage. The scenario is simple: just dynamically add a button to a form. The only special thing about this button is that it will be an UltraButton from Infragistics NetAdvantage.
Here is how the button is added to the form (UltraButtonForm.cs):
_ultraButton = new UltraButton();
_ultraButton.Text = "UltraButton";
_ultraButton.Location = new Point(10, 100);
Controls.Add(_ultraButton);
Nothing fancy here. We just create a new instance of UltraButton and add it to the list of controls of the form so that it gets displayed. This works fine. Even when the form is closed/disposed, everything gets released.
Now, let's do one more little thing: remove the button before closing the form.
Controls.Remove(_ultraButton);
Guess what: now there is a problem! And potentially a big one.
The problem is that the UltraButton is not released from memory this time. And it's not alone, as I'll show soon.
As you can see, the button is kept alive by an event from the Infragistics.Win.Office2007ColorTable class named ColorSchemeChanged. This happens because this is a static event and UltraButton uses its Dispose method to unsubscribe from this event.
Without the call to Controls.Remove, everything was fine because all the controls contained in the Controls property of a Windows Forms Control are automatically disposed. When a control is removed from the Controls collection, it won't be disposed unless you do it explicitely yourself.
Here, the leak is due to a static event named ColorSchemeChanged, or one of its friends. The only way to ensure that such references get released is to make sure that Dispose is invoked on ALL the Infragistics controls. This is achieved via invoking Dispose on all the other controls.
If you use Infragistics NetAdvantage you may already have seen ColorSchemeChanged, Office2007ColorTable, and their friend, and maybe thought that there was a bug in them that caused leaks. As you have seen, this is not a bug per se, but it can be a source of memory leaks if you don't pay careful attention to ensure that all controls get disposed.
So, we've just seen again what I exposed above with static events. What I want to stress here is that it's easy to introduce a leak with just a line of code. Would you have thought about memory leaks when adding the call to Controls.Remove?
An UltraButton may not be a big object, but if you have several of them and if you do the same operation multiple times, you'll start to see a bigger leak. In addition, this doesn't happen only with UltraButton, but also with most of the other Infragistics controls, and potentially with other objects that subscribe to static events and depend on Dispose for unsubscribing. The only way to ensure that all objects get fully released is to make sure that Dispose is invoked on all controls.
Sample source code
The MVVM pattern (Model-View-ViewModel, also known as M-V-VM, the PresentationModel pattern, or the ViewModel pattern) appeared a long time ago now, but it took some time before it became more documented and well known. Over the last few months, MVVM boomed, especially with an explosion of dedicated frameworks. It's difficult to know what each framework proposes and which one to choose. Fortunately, Jeremy Alles has compiled pointers to all the MVVM frameworks available today. He even lists the main features of each framework.
Jeremy Alles proposes a nice tour of the core WPF classes. It comes with a class diagram and quick descriptions.
I'll keep a link to it here for reference. It's the kind of document I'll refer to several times.
Covered classes include DispatcherObject, FrameworkTemplate, DependencyObject, Visual, UIElement, FrameworkElement, Control, and much more.
My new article, which I've been preparing over the last few months, is now available. It's about memory and resources leaks in .NET.
You'll find it in English on MSDN and in French on DotNetGuru.org. It's also available in PDF in English and in French.
Here is the table of content:
- Introduction
- Leaks? Resources? What do you mean?
- How to detect leaks and find the leaking resources
- Common memory leak causes
- Common memory leaks causes demonstrated
- How to avoid leaks
- Tools
- Conclusion
- Resources
Despite what a lot of people believe, it's easy to introduce memory and resources leaks in .NET applications. The Garbage Collector, or GC for close friends, is not a magician who would completely relieve you from taking care of your memory and resources consumption.
I'll explain in this article why memory leaks exist in .NET and how to avoid them. Don't worry, I won't focus here on the inner workings of the garbage collector and other advanced characteristics of memory and resources management in .NET.
It's important to understand leaks and how to avoid them, especially since they are not the kind of things that is easy to detect automatically. Unit tests won't help here. And when your application crashes in production, you'll be in a rush looking for solutions. So, relax and take the time to learn more about this subject before it's too late.
Mon nouvel article, que j'ai préparé durant ces derniers mois, est maintenant disponible. Il parle des fuites de mémoire et de ressources en .NET.
Vous le trouverez en français sur DotNetGuru.org et en anglais sur MSDN. Il est également disponible en PDF en français et en anglais.
Voici la table des matières :
- Introduction
- Fuites ? Ressources ? Qu'entendez-vous par là ?
- Comment détecter des fuites et repérer les ressources qui fuient
- Les causes habituelles de fuites de mémoire
- Les causes habituelles de fuites en pratique
- Comment éviter les fuites
- Outils
- Conclusion
- Ressources
En dépit de ce que beaucoup de personnes pensent, il est facile d'introduire des fuites de mémoire et de ressources dans les applications .NET. Le Garbage Collector, également appelé ramasse-miettes ou GC pour les intimes, n'est pas un magicien qui vous affranchirait totalement de vous soucier de la consommation de ressources et de mémoire.J'expliquerai dans cet article pourquoi les fuites de mémoire existent en .NET et comment les éviter. Ne vous en faites pas, je ne vais pas me focaliser ici sur le fonctionnement interne du garbage collector et autres caractéristiques avancées de la gestion de la mémoire et de ressources en .NETIl est important de comprendre les fuites et comment les éviter, particulièrement car elles n'entrent pas dans la catégorie des choses facilement détectables de manière automatisée. Les test unitaires ne vous aideront pas ici. Et quand votre application se plantera en production, vous serez à la recherche de solutions dans l'urgence. Alors, détendez-vous et prenez le temps dès maintenant d'en apprendre plus sur ce sujet avant qu'il ne soit trop tard.
fivesecondtest.com, "A simple online usability test that helps you identify the most prominent elements of your user interfaces."
Simple yet great idea.
Here is an example: Help me improve a design I'm working on by doing a five second test.
I've just moved the LINQ tools referenced on SharpToolbox.com to a new dedicated LINQ category.
20 tools and providers are listed for the moment. I'll add more shortly.
See Jim's blog for more resources.
Cross-posted from http://linqinaction.net
Ten years ago, I played with OLE Automation so we can write things such as the following, where Document represents an XML document:
String name = Document.Bookstore.Book[1].Author.LastName;
Document.Bookstore.Book[1].Author.LastName = "NewName";
The experiment was named XMLAuto.
I wanted to implement this again with C# 4's dynamic keyword, but I won't have to do it. Mark Michaelis and Michael Stokesbary already played with this (last year).
I find it's an interesting way to learn how to implement IDynamicObject.
Update: See also this article about the ExpandoObject and this article about DynamicObject.
Given my own experience, I'd say that events are the main source of leaks in .NET. They deserve double- and even triple-checks. Each time you add a subscription to an event in your code, most likely with +=, you should worry about the consequences and ask yourself whether you need to add a -= somewhere to unsubscribe from the event. If you have to, do it immediately before you forget about it. Often, you'll do that in a Dispose method.
Don't forget that subject objects keep the observer (or listener, or subscriber) objects that observe them alive. See this post of mine if you need a refresher about the subject.
Having listener objects unsubscribe from the events they subscribe to is usually the recommended way to ensure they can be collected. However, when you absolutely know that a subject object wont publish notifications anymore and you wish that its subscribers can be released, you can force the removal of all the subscriptions to the subject object. Here is how this can be achieved:
if (SomeEvent != null)
{
foreach (EventHandler handler in SomeEvent.GetInvocationList())
SomeEvent -= handler;
}
Update: As suggested by Steve in a comment, SomeEvent = null is enough!
Update: I've improved the sample source code to show if the listeners are dead or alive.
Update: See also this new article of mine.
I have a code sample available for you to download.
"Error creating window handle"
When a big Windows Forms application I'm working on for a client is used actively, users often get "Error creating window handle" exceptions.
Aside from the fact that the application consumes too much resources, which is a separate issue altogether that we are already addressing, we had difficulties with determining what resources were getting exhausted as well as what the limits are for these resources.
We first thought about keeping an eye on the Handles counter in the Windows Task Manager. That was because we noticed that some processes tended to consume more of these resources than they normally should. However, this counter is not the good one because it keeps track of resources such as files, sockets, processes and threads. These resources are named Kernel Objects.
The other kinds of resources that we should keep an eye on are the GDI Objects and the User Objects. You can get an overview of the three categories of resources on MSDN.
User Objects
Window creation issues are directly related to User Objects.
We tried to determine what the limit is in terms of User Objects an application can use.
There is a quota of 10,000 user handles per process. This value can be changed in the registry, however this limit was not the real show-stopper in our case.
The other limit is 66,536 user handles per Windows session. This limit is theoretical. In practice, you'll notice that it can't be reached. In our case, we were getting the dreaded "Error creating window handle" exception before the total number of User Objects in the current session reached 11,000.
Desktop Heap
We then discovered which limit was the real culprit: it was the "Desktop Heap".
By default, all the graphical applications of an interactive user session execute in what is named a "desktop". The resources allocated to such a desktop are limited (but configurable).
Note: User Objects are what consumes most of the Desktop Heap's memory space. This includes windows.
For more information about the Desktop Heap, you can refer to the very good articles published on the NTDebugging MSDN blog:
Desktop Heap Monitor (dheapmon.exe)
It's possible to monitor the Desktop Heap usage thanks to a command line tool: Desktop Heap Monitor (dheapmon.exe).
It would be interesting to monitor this usage directly from within applications to prevent crashes. We could let users know that all the resources are about to be exhausted, and ask them to close windows and prevent them from opening new screens. This would help to avoid the "Error creating window handle" errors. When these exceptions occur, it's difficult to handle them gracefully and it's often too late to react because the application is in an unstable state.
Unfortunately, it's not possible to consult the usage of the desktop heap programmatically from an application. The dheapmon.exe tool is based on a kernel mode driver (a .sys file) for collecting the data it returns.
A solution could be to create a new desktop, dedicated to the application. In practice, this is not viable though because only one desktop can be visible at a time.
Another solution is to increase the size of the Desktop Heap. We can, for example, replace the second value by default of SharedSection (3072) by 4096. Yay, more resources to waste!
What's the real solution? Be green!
Increasing the Desktop Heap is an effective solution, but that's not the ultimate one. The real solution is to consume less resources (less window handles in our case). I can guess how disappointed you can be with this solution. Is this really all what I can come up with??
Well, there is no big secret here. The only way out is to be lean. Having less complicated UIs is a good start. It's good for resources, it's good for usability too. The next step is to avoid waste, to preserve resources, and to recycle them!
Here is how we're doing this in my client's application:
- We use TabControls and we create the content of each tab on the fly, when it becomes visible;
- We use expandable/collapsible regions, and again fill them with controls and data only when needed;
- We release resources as soon as possible (using the Dispose method). When a region is collapsed, it's possible to clear it's child controls. The same for a tab when it becomes hidden;
- We use the MVP design pattern, which helps in making the above possible because it separates data from views;
- We use layout engines, the standard FlowLayoutPanel and TableLayoutPanel ones, or custom ones, instead of creating deep hierarchies of nested panels, GroupBoxes and Splitters (an empty splitter itself consumes three window handles...).
The above are just hints at what you can do if you need to build rich Windows Forms screens. There's not doubt that you can find other approaches.
The first thing you should do in my opinion is building your applications around use cases and scenarios. This helps in displaying only what's needed at a given time, and for a given user.
Of course, another solution would be to use a system that doesn't rely on handles... WPF anyone?
After English, German, Spanish, and Portuguese, LINQ in Action is now available in Chinese (中文).
The title is LINQ实战. The publisher is 人民邮电出版社 (Posts & Telecom Press). The book is also available on Amazon.cn/joyo and several other bookstores.
In addition to all the translations, LINQ in Action has been republished in English in India by DreamTech Press.
Cross-posted from http://linqinaction.net
More Posts
Next page »