By Fabrice Marguerie

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.

Table of Content

Introduction

Recently, I've been working on a big .NET project (let's name it project X) for which one of my duties was to track memory and resources leaks. I was mostly looking after leaks related to the GUI, more precisely in a Windows Forms application based on the Composite UI Application Block (CAB).
While some of the information that I'll expose here applies directly to Windows
Forms, most of the points will equally apply to any kind of .NET applications
(WPF, Silverlight, ASP.NET, Windows service, console application, etc.)

I wasn't an expert in leak hunting before I had to delve into
the depths of the application to do some cleaning. The goal of the present
article is to share with you what I learned in the process. Hopefully, it will
be useful to anyone who needs to detect and fix memory and resources leaks.
We'll start by giving an overview of what leaks are, then we'll see how to
detect leaks and find the leaking resources, how to solve and avoid leaks, and
we'll finish with a list of helpful tools and...resources.

Leaks?
Resources? What do you mean?

Memory
leaks

Before going further, let's defined what I call a "memory
leak". Let's simply reuse the definition found in Wikipedia.
It perfectly matches what I intend to help you solve with this article:


In computer science, a
memory leak is a particular type of unintentional memory consumption by a
computer program where the program fails to release memory when no longer
needed. This condition


is normally the result of a bug in a program that prevents
it from freeing up memory that it no longer needs.

Still in Wikipedia: "Languages that provide automatic
memory management, like Java, C#, VB.NET or LISP, are not immune to memory
leaks."

The garbage collector recovers only memory that has become
unreachable. It does not free memory that is still reachable. In .NET, this
means that objects reachable by at least one reference won't be released by the
garbage collector.

Handles and
resources

Memory is not the only resource to keep an eye on. When your
.NET application runs on Windows, it consumes a whole set of system resources.
Microsoft defines three categories of system objects: user, graphics
device interface (GDI), and kernel. I won't give here the complete list of
objects. Let's just name important ones:

  • The system uses User objects to support window
    management. They include: Accelerator tables, Carets, Cursors, Hooks,
    Icons, Menus and Windows.

  • GDI objects

    support graphics: Bitmaps, Brushes, Device Contexts (DC), Fonts, Memory
    DCs, Metafiles, Palettes, Pens, Regions, etc.


  • Kernel objects

    support memory management, process execution, and
    inter-process communications (IPC): Files, Processes, Threads, Semaphores,
    Timers, Access tokens, Sockets, etc.

You can find all the
details about system objects on MSDN
.

In addition to system objects, you'll encounter handles.
As stated on MSDN, applications cannot directly access object data or the
system resource that an object represents. Instead, an application must obtain
an object handle, which it can use to examine or modify the system resource.
In .NET however, this will be transparent most of the time because system
objects and handles are represented directly or indirectly by .NET classes.

Unmanaged
resources

Resources such as system objects are not a problem in
themselves, however I cover them in this article because operating systems such
as Windows have limits on the number of sockets, files, etc. that can be open
simultaneously. That's why it's important that you pay attention to the
quantity of system objects you application uses.

Windows has quotas for the number of User and GDI objects that
a process can use at a given time. The default values are 10,000 for GDI
objects and 10,000 for User objects. If you need to read the values set on your
machine, you can use the following registry keys, found in
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows:
GDIProcessHandleQuota and USERProcessHandleQuota.

Guess what? It's not even that simple. There are other limits
you can reach quickly. See my
blog post about the Desktop Heap
, for example.

Given that these values can be customized, you may think that
a solution to break the default limits is to raise the quotas. I think that
this is a bad idea for several reasons:

  1. Quotas
    exist for a reason: your application is not alone on the system and it should
    share the system resources with the other processes running on the machine.
  2. If
    you change them
    , they may be different on another one. You have to make
    sure that the change is done on all the machines your application will run on,
    which doesn't come without issues from a system administration point of view.
  3. The
    default quotas are
    most of the
    time. If you find the quotas are not enough for your application, then you
    probably have some cleaning to do.

How to
detect leaks and find the leaking resources

The real problem with leaks is well stated in an article about
leaks with GDI code
:


Even a
little leak can bring down the system if it occurs many times.

This is similar to what happens with leaking water. A drop of
water is not a big issue. But drop by drop, a leak can become a major problem.

As I'll explain later, a single insignificant object can
maintain a whole graph of heavy objects in memory.

Still in the same article, you can learn that:


There are
usually three steps in leak eradication:


  1. Detect
    a leak

  2. Find
    the leaking resource

  3. Decide
    where and when the resource should be released in the source code

The most direct way to "detect" leaks is to suffer
from them

You won't likely see your computer run out of memory.
"Out of memory" messages are quite rare. This is because when
operating systems run out of RAM, they use hard disk space to extend the memory
workspace (this is called virtual memory).

What you're more likely to see happen are "out of
handles" exceptions in your Windows graphical applications. The exact
exception is either a System.ComponentModel.Win32Exception or a System.OutOfMemoryException
with the following message: "Error
creating window handle"
. This happens when two many resources are
consumed at the same time, most likely because of objects not being released
while they should.

Another thing you may see even more often is your application
or the whole computer getting slower and slower. This can happen because your
machine is simply getting out of resources.

Let me make a blunt assertion: most applications leaks. Most
of the time it's not a problem because the issues resulting from leaks show up
only if you use applications intensively and for a long period of time.

If you suspect that objects are lingering in memory while they
should have been released, the first thing you need to do is to find what these
objects are.

This may seem obvious, but what's not so obvious is how to
find these objects.

What I suggest is that you look for unexpected and lingering
high level objects or root containers with your favorite memory profiler. In
project X, this can be objects such as LayoutView instances (we use the MVP pattern
with CAB/SCSF). In your case, it all depends on what the root objects are.

The next step is to find why these objects are being kept in
memory while they shouldn't be. This is where debuggers and profilers really
help. They can show you how objects are linked together.
It's by looking at the incoming references to the zombie object you have
identified that you'll be able to find the root cause of the problem.

You can choose to follow the the
ninja way
(See SOS.dll and WinDbg in the section
about tools below
).
I used the JetBrains dotTrace tool for project X and that's what I'll use in
this article too. I'll tell you more about this tool later in the same Tools section.

Your goal should be to find the root reference. Don't stop at
the first object you'll find, but ask yourself why this object is kept in
memory.

Common
memory leak causes

I wrote above that leaks are common in .NET. The good news is
that there is only a small set of causes. That means that you won't have to
look for a lot of cases when you'll try to solve a leak.

Let's review the usual culprits I've identified:

  • Static references
  • Event with missing unsubscription
  • Static event with missing unsubscription
  • Dispose method not invoked
  • Incomplete Dispose method

In addition to these classical traps, here are other more
specific problem sources:

  • Windows Forms: BindingSource misused
  • CAB: missing Remove call on WorkItem

The culprits I've just listed concern your applications, but
you should understand that leaks can happen in other pieces of .NET code that
your applications rely on. There can actually be bugs in libraries you use.

Let's take an example. In project X, a third-party visual
controls suite is used to build the GUI. One of these controls is used to
display toolbars. The way it is used is via a component that manages a list of
toolbars. This works fine, except that even though the toolbar class implements
IDisposable, the manager class never calls the Dispose method on the toolbars
it manages. This is a bug. Fortunately, a workaround is easy to find: just call
Dispose by ourselves on each toolbar. Unfortunately, this is not enough because
the toolbar class 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.
Anyway, this is just a specific example. My point is that any libraries and
components you use may cause leaks in your applications.

Finally, I'd like to add that saying "It's the .NET
framework that leaks!" is adopting a very bad stance that consists in
washing your hands of it. Even if it can of course happen that
.NET creates leaks by itself
, it's something that remains exceptional and
that you'll encounter very rarely.
It is easy to blame .NET, but you should instead start by questioning your own
code before offloading issues onto someone else...

Common
memory leaks causes demonstrated

I've listed the main sources of leaks, but I don't want to
stop here. I think that this article will be much more useful if I can
illustrate each point with a quick example. So, let's take Visual Studio and
dotTrace and walk through some sample code. I'll show at the same time how to
solve or avoid each leak.

Project X is built with CAB and the MVP (Model-View-Presenter)
pattern, that means that the GUI consists of workspaces, views and presenters.
To keep things simple, I've decided to use a basic Windows Forms application
with a set of forms. This is the same approach as the one used by Jossef
Goldberg in his
post about memory leaks in WPF-based applications
. I'll even reuse the same
examples for event handlers.

When a form is closed and disposed, we expect it to be
released from memory, right? What I'll show below is how the causes I listed
above prevent the forms to be released.

Here is the main form of the sample application I've created:

 

This main form can open different child forms; each can cause
a separate memory leak.

You'll find the source code of this sample application at the
end of this article, in the Resources section.

Static
references

Let's get rid of the obvious first. If an object is referenced
by a static field, then it will never be released.
This is also true with such things as singletons. Singletons are often static
objects, and if it's not the case, they are usually long-lived objects anyway.

This may be obvious, but keep in mind that not only direct
references are dangerous. The real danger comes from indirect references. In
fact, you must pay attention to the chains of references. What counts is the
root of each chain. If the root is static, then all the objects down the chain
will stay alive forever.

 

If Object1 on the above diagram is static, and most likely
long-lived, then all the other objects down the reference chain will be kept in
memory for a long time. The danger is that the chain can be too long to realize
that the root of the chain is static. If you care about only one level of depth,
you will consider that Object3 and Object4 will go away when Object2 goes away.
That's correct, for sure, but you need to take into account the fact that they
may never go away because Object1 keeps the whole object graph alive.

Be very careful with all kinds of statics. Avoid them if
possible. If not, pay careful attention to the objects your static objects and
singletons keep in memory.

A specific kind of risky statics are static events. I'll cover
them just after I cover events in general.

Events, or
the "lapsed listener" issue

A child form is subscribing to an event of the main form to
get notified when the opacity changes (EventForm.cs):

C#

 
mainForm.OpacityChanged += mainForm_OpacityChanged;

The problem is that the subscription to the OpacityChanged event creates a reference from the main form to the child form.

Here is how objects are connected after the subscription:

 

See this post of mine to learn more about events and references. Here is a figure from this post that shows the "back" reference from a subject to its observers:

 

Here is what you'll see in dotTrace if you search for EventForm and click on "Shortest" in "Root Paths":

 

As you can see, MainForm keeps a reference to EventForm. This is the case for each instance of EventForm that you'll open in the application. This means that all the child forms that you'll open will stay in memory while the application is alive, even if you don't use them anymore.

Not only does this maintain the child forms in memory, but it also causes exceptions if you change the opacity after a child form has been closed because the main form tries to notify a disposed form.

The most simple solution is to remove the reference by having the child forms unsubscribe from the main form's event when they get disposed:

C#
 
Disposed += delegate { mainForm.OpacityChanged -= mainForm_OpacityChanged; };

Nota Bene: We have a problem here because the MainForm object remains alive until the application is shut down. Interconnected objects with shorter lifetimes may not cause issues with memory. Any isolated graph of objects gets unloaded automatically from memory by the garbage collector. An isolated graph of objects is formed by two objects that only reference one another, or by a group of connected objects without any external reference.

Another solution would be to use weak delegates, which are based on weak references. I touch this subject in my post about events and references. Several articles on the Web demonstrate how to put this into action. Here is a good one, for example. Most of the solutions you'll find are based on the WeakReference class. You can learn more about weak references in .NET on MSDN.

Note that a solution for this exists in WPF, in the form of the WeakEvent pattern.

There are other solutions if you work with frameworks such as in CAB (Composite UI Application Block) or Prism (Composite Application Library), respectively EventBroker and EventAggregator. If you want, you can also use your own implementation of the event broker/aggregator/mediator pattern.

Event handlers with missing unsubscriptions from events on static or long-lived objects are a problem. Another one is static events.

Static events

Let's see an example directly (StaticEventForm.cs):

C#
 
SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;

This is similar to the previous case, except that this time we subscribe to a static event. Since the event is static, the listener form object will never get released.

 

Again, the solution is to unsubscribe when we're done:

C#
 
SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged;

Dispose method not invoked

You've paid attention to events, static or not? Great, but that's not enough. You can still get lingering references even with correct cleanup code. This happens sometimes simply because this cleanup code doesn't get invoked...

Using the Dispose method or the Disposed event to unsubscribe from event and to release resources is a best practice, but it's useless if Dispose doesn't get called.

Let's take an interesting example. Here is sample code that creates a context-menu for a form (ContextMenuStripNotOKForm.cs):

C#
 
ContextMenuStrip menu = new ContextMenuStrip(); menu.Items.Add("Item 1"); menu.Items.Add("Item 2"); this.ContextMenuStrip = menu;

Here is what you'll see with dotTrace after the form has been closed and disposed:

 

The ContextMenuStrip is still alive in memory! Note: To see the problem happen, show the context-menu with a right-click before closing the form.

Again this is a problem with static events. The solution is the same as usual:

C#
 
Disposed += delegate { ContextMenuStrip.Dispose(); };

I guess you start to understand how events can be dangerous in .NET if you don't pay careful attention to them and the references they imply.
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 potential memory leaks when creating
a context-menu?

It's even worst than what you imagine. Not only the
ContextMenuStrip is maintained alive, but it maintains the complete form alive
with it!
In the following screenshot, you can see that the ContextMenuStrip references
the form:

 

The result is that the form will be alive as long as the
ContextMenuStrip is. Oh, of course you should not forget that while the form is
alive, it maintains itself a whole set of objects alive - the controls and
components it contains, to start with:

 

This is something that I find important enough to warrant a
big warning. A small object can potentially maintain big graphs of other
objects in memory. I've seen this happen all the time in project X. This is the
same with water: a small leak can cause big damages.

Because a single control points to its parent or to events of
its parent, it has the potential to keep a whole chain of container controls
alive if it hasn't been disposed. And of course, all the other controls
contained in these containers are also kept in memory. This can for example
lead to a complete form and all its content that remain in memory for ever (at
least until the application stops).

At this point, you may be wondering if this problem always
exists with ContextMenuStrip. It doens't. Most of the time, you create
ContextMenuStrips with the designer directly on their form, and in this case
Visual Studio generates code that ensures the ContextMenuStrip components get
disposed correctly.

If you're interested in knowing how this is handled, you can
take a look at the ContextMenuStripOKForm class and its components
field in the ContextMenuStripOKForm.Designer.cs file.

I'd like to point out another situation I've seen in project
X. For some reason there was no .Designer.cs files associated with the source
files of some controls. The designer code was directly in the .cs files. Don't
ask me why. Besides the unusual (and not recommended) code structure, the
problem was that the designer code had not been completely copied: either the
Dispose method was missing or the call to components.Dispose was
missing. I guess you understand the bad things that can happen in these cases.

Incomplete
Dispose method

I guess that you've now understood the importance of calling
Dispose on all objects that have this method. However, the is one thing I'd
like to stress about Dispose. It's great to have your classes implement the
IDisposable interface and to include calls to Dispose and using blocks
all over your code, but that's really useful only if the Dispose methods are
implemented correctly.

This remark may seem a bit stupid, but if I make it it's
because I've seen many cases where the code of Dispose methods was not
complete.

You know how it happens. You create your classes; you have
them implement IDisposable; you unsubscribe from events and release resources
in Dispose; and you call Dispose everywhere. That's fine, until later on you
have one of your classes subscribe to a new event or consume a new resource.
It's easy to code, you're eager to finish coding and to test your code. It runs
fine and you're happy with it. You checkin. Great! But... oops, you've
forgotten to update Dispose to release everything. It happens all the time.

I won't provide an example for this. It should be pretty
obvious.

Windows
Forms: BindingSource misused

Let's address an issue specific to Windows Forms. If you use
the BindingSource component, be sure you use it the way it has been designed to
be.

I've seen code that exposed BindingSource via static
references. This leads to memory leaks because of the way a BindingSource
behaves. A BindingSource keeps a reference to controls that use it as their
DataSource, even after these controls have been disposed.

Here is what you'll see with dotTrace after a ComboBox has
been disposed if its DataSource is a static (or long-lived) BindingSource
(BindingSourceForm.cs):

 

A solution to this issue is to expose a BindingList instead of
a BindingSource. You can, for example, drop a BindingSource on your form,
assign the BindingList as the DataSource for the BindingSource, and assign the
BindingSource as the DataSource for the ComboBox. This way, you still use a
BindingSource.

See BindingListForm.cs in the sample source code to see this
in action.

This doesn't prevent you from using a BindingSource, but it
should be created in the view (the form here) that uses it. That makes sense
anyway: BindingSource is a presentation component defined in the
System.Windows.Forms namespace. BindingList, in comparison, is a collection
that's not attached to visual components.

Note: If you don't really need a BindingSource, you can simply
use just a BindingList all the way.

CAB:
Removal from WorkItem missing

Here is now an advice for CAB applications, but that you can
apply to other kinds of applications.

WorkItems are central when building CAB applications. A
WorkItem is a container that keeps track of alive objects in a context, and
performs dependency injection. Usually, a view gets added to a WorkItem after
it's created. When the view is closed and should be released, it should be
removed from its WorkItem, otherwise the WorkItem will keep the view alive
because it maintains a reference to it.

Leaks can happen if you forget to remove views from their
WorkItem.

In project X, we use the MVP design pattern
(Model-View-Presenter). Here is how the various elements are connected when a
view is displayed:

 

Note that the presenter is also added to the WorkItem, so it
can benefit from dependency injection too. Most of the time, the presenter is
injected to the view by the WorkItem, by the way. To ensure that everything
gets released properly in project X, we use a chain-of-responsibility as
follows:

 

When a view is disposed (most likely because it has been
closed), its Dispose method is invoked. This method in turn invokes the Dispose
method of the presenter. The presenter, which knows the WorkItem, removes the
view and itself from the WorkItem. This way, everything is disposed and
released properly.

We have base classes that implement this
chain-of-responsibility
in our application framework, so that views
developers don't have to re-implement this and worry about it every time. I
encourage you to implement this kind of pattern in your applications, even if
they are not CAB applications. Automating release patterns right into your
objects will help you avoid leaks by omission.
It will also ensure that
this processing is implemented only in one way, and not differently by each
developer because she/he may not know a proper way of doing it - which could
lead to leaks by lack of know-how.

How to
avoid leaks

Now that you know more about leaks and how then can happen,
I'd like to stress a few points and give you some tips.

Let's first discuss about a general rule. Usually, an object
that creates another object is responsible for disposing it. Of course, this is
not the case if the creator is a factory.

The reverse: an object that receives a reference to another
object is not responsible for disposing it.

In fact, this really depends on the situation. In any case,
what's important to keep in mind is who owns an object.

Second rule: Each += is a potential enemy!

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, you should worry about the
consequences and ask yourself whether you need to add a -= 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.

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 an observed 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 observed object. I have a
code sample on my blog
that shows how to do this.

A quick tip now. Often, issues start to appear when object
references are shared among several objects. This happens because it may become
difficult to keep track of what references what. Sometimes it's better to clone
objects in memory and have views work with the clones rather than having views
and model objects intertwined.

Finally, even if it's a well-known best practice in .NET, I'd
like to stress one more time the importance of calling Dispose. Each time you
allocate a resource, make sure you call Dispose or encapsulate the use of the
resource in a using block. If you don't do this all the time, you can
quickly end up with leaking resources - most of the time unmanaged resources.

Tools

Several tools are available to help you track object
instances, as well as system objects and handles. Let's name a few.

Bear

Bear
is a free program that displays for all processes running under Windows:

  • the usage of all GDI objects (hDC, hRegion, hBitmap,
    hPalette, hFont, hBrush)
  • the usage of all User objects (hWnd, hMenu, hCursor,
    SetWindowsHookEx, SetTimer and some other stuff)
  • the handle count
 

GDIUsage

Another useful tool is GDIUsage.
This tool is free too and comes with source code.

GDIUsage focuses on GDI Objects. With it, you can take a
snapshot of the current GDI consumption, perform an action that might cause
leaks, and make a comparison between the previous resources usage and the
current one. This helps a lot because it allows you to see what GDI objects
have been added (or released) during the action.

 

In addition, GDIUsage doesn't only give you numbers, but can
also provide a graphical display of the GDI Objects. Visualizing what bitmap is
leaked makes it easier to find why it has been leaked.

 

dotTrace

JetBrains dotTrace
is a memory and performance profiler for .NET.

The screenshots I used in this article have been taken with
dotTrace. This is also the tool I used the most for project X. I don't know the
other .NET profilers well, but dotTrace provided me with the information I
needed to solve the leaks detected in project X - more than 20 leaks so far...
did I tell you that it's a big project?

dotTrace allows you to identify which objects are in memory at
a given moment in time, how they are kept alive (incoming references), and
which objects each object keeps alive (outgoing references). You also get
advanced debugging with allocations stack traces, the list of dead objects,
etc.

 

Here is a view that allows you to see the differences between
two memory states:

 

dotTrace is also a performance profiler:

 

The way you use dotTrace, is by launching it first, then you
ask it to profile your application by providing the path to the .EXE file.

If you want to inspect the memory used by your application,
you can take snapshots while it's running and ask dotTrace to show you
information. The first things you'll do are probably ask dotTrace to show you
how many instances of a given class exist in memory, and how they are kept
alive.

In addition to searching for managed instances, you can also
search for unmanaged resources. dotTrace doesn't offer direct support for
unmanaged resources tracking, but you can search for .NET wrapper objects. For
example, you can see if you find instances of the Bitmap, Font or Brush
classes. If you find an instance of such a class that hasn't been disposed,
then the underlying system resource is still allocated to your application.

The next tool I'll present now offers built-in support for
tracking unmanaged resources. This means that with it you'll be able to search
directly for HBITMAP, HFONT or HBRUSH handles.

.NET Memory
Profiler

.NET
Memory Profiler
is another interesting tool. Useful features it offers that
you don't get with dotTrace include:

  • View objects that have been disposed but are still
    alive
  • View objects that have been released without having
    been disposed
  • Unmanaged resources tracking
  • Attach to a running process
  • Attach to a process at the same time as Visual Studio's
    debugger
  • Automatic memory analysis (tips and warnings regarding
    common memory usage issues)
 

Many .NET
profilers are available

The above tools are just examples of what is available to help
you. dotTrace and .NET Memory Profiler are two of the several memory (and
performance) profilers available for .NET. Other big names include ANTS Profiler, YourKit Profiler,
PurifyPlus, AQtime and CLR
Profiler
. Most of these tools offer the same kind of services as dotTrace.
You'll find a whole set of tools dedicated
to .NET profiling on SharpToolbox.com
.

SOS.dll and
WinDbg

Another tool you can use is SOS.dll. SOS.dll is a
debugging extension that helps you debug managed programs in the WinDbg.exe
debugger and in Visual Studio by providing information about the internal CLR
(common language runtime) environment. SOS can be used to get information about
the garbage collector, objects in memory, threads and locks, call stacks and
more.


WinDbg

is the tool you'll use most
often when attaching to a process in production. You can learn more about
SOS.dll and WinDBg on Rico
Mariani's blog
, on Mike Taulty's blog (SOS.dll
with WinDbg
and SOS.dll
with Visual Studio
), and on Wikipedia.

SOS.dll and WinDbg are provided free-of-charge by Microsoft as
parts of the Debugging
Tools for Windows
package. One advantage of SOS.dll and WinDbg compared to
the other tools I listed is their low resources consumption, while remaining
powerful.

Sample output with sos.dll and the gcroot command:

 

WinDbg screenshot:

 

Custom
tooling

In addition to tools available on the market, don't forget
that you can create your own tools. Those can be standalone tools that you'd
reuse for several of your applications, but they can be a bit difficult to
develop.

What we did for project X is develop integrated tools that
help us keep track in real-time of resource usage and potential leaks.

One of these tools displays a list of alive and dead objects,
right from the main application. It consists of a CAB service and a CAB view
that can be used to check whether objects we expect to be released have indeed
been released.
Here is a screenshot of this tooling:

 

Keeping track of each object of the application would be too
expensive and anti-productive given the number of objects involved in a large
application. In fact, we don't keep an eye on all objects, but only on the high
level objects and root containers of the application. Those are the objects I
advised to track when I explained how to detect leaks.

The technique we used for creating this tool is quite simple.
It uses weak references. The
WeakReference class
allows you to reference an object while still allowing
that object to be reclaimed by garbage collection. In addition, it allows you
to test whether the referenced object is dead or alive, via the
IsAlive property
.

In project X, we also have a widget that provides an overview
of the GDI and User objects usage:

 

When resources are nearing exhaustion, the widget reports it
with a warning sign:

 

In addition, the application may ask the user to close some of
the currently opened windows/tabs/documents, and prevent her/him from opening
new ones, until the resources usage gets back below the critical level.

In order to read the current UI resources usage, we use the
GetGuiResources function
from User32.dll. Here is how we import it in C#:

C#

 
// uiFlags: 0 - Count of GDI objects
// uiFlags: 1 - Count of USER objects
// GDI objects: pens, brushes, fonts, palettes, regions, device contexts, bitmaps, etc.
// USER objects: accelerator tables, cursors, icons, menus, windows, etc.
[DllImport("User32")]
extern public static int GetGuiResources(IntPtr hProcess, int uiFlags);
public static int GetGuiResourcesGDICount(Process process)
{
  return GetGuiResources(process.Handle, 0);
}
public static int GetGuiResourcesUserCount(Process process)
{
  return GetGuiResources(process.Handle, 1);
}

We retrieve the memory usage via the Process.GetCurrentProcess().WorkingSet64 property.

Conclusion

I hope that this article has provided you with a good base for improving your applications and for helping you solve leaks. Tracking leaks can be fun... if you don't have anything better to do with your time :-) Sometimes, however, you have no choice because solving leaks is vital for your application.

Once you have solved leaks, you still have work to do. I strongly advise you to improve your application so that it consumes as less resources as possible. Without losing functionality, of course. I invite you to read my recommendations at the end of this blog post.

Resources

The source code for the demonstration application is available for download.

Here are some interesting additional resources if you want to dig further:

Version française de cet article

How to detect and avoid memory and resources leaks in .NET applications的更多相关文章

  1. Windbg and resources leaks in .NET applications 资源汇总

    Windows Forms Leaks 1.http://blogs.msdn.com/b/tess/archive/2008/02/04/net-debugging-demos-informatio ...

  2. e.next = nil // avoid memory leaks e.prev = nil // avoid memory leaks

    /Go/src/container/list/list.go:10

  3. Min Stack (LeetCode) tweak it to avoid Memory Limit Exceeded

    class MinStack { public: void push(int x) { if(values.empty()) { values.push_back(x); min_indices.pu ...

  4. MEF引起的内存泄露

    也许你编程的时候很小心,注意不引起内存泄露,例如不要被全局Static的变量引用上,注意Singleton的static引用,注意Event Handler注销,注意IDisposable接口实现,而 ...

  5. An Autofac Lifetime Primer

    Or, “Avoiding Memory Leaks in Managed Composition” Understanding lifetime can be pretty tough when y ...

  6. DTrace to Troubleshoot Java Native Memory Problems

    How to Use DTrace to Troubleshoot Java Native Memory Problems on Oracle Solaris 11 Hands-On Labs of ...

  7. Maintaining processor resources during architectural events

    In one embodiment of the present invention, a method includes switching between a first address spac ...

  8. Satisfying memory ordering requirements between partial reads and non-snoop accesses

    A method and apparatus for preserving memory ordering in a cache coherent link based interconnect in ...

  9. Diagnosing out of memory errors and memory leaks 内存泄露实例 C Java JavaScript 内存泄露

    小结: 1. 数据库连接池. JDBC语句和结果对象必须显式地关闭. 2. 电梯到目标楼层后地址是否被释放 When a button is pressed: Get some memory, whi ...

随机推荐

  1. linux修改时间 时区

    查看时区:date -R 修改时区  cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 二.时间 1.查看时间和日期 date 2.设置时间和日期 ...

  2. div+css实现未知宽高元素垂直水平居中

    div+css实现未知宽高元素垂直水平居中.很多同学在面试的时候都会遇到这样的问题:怎么用div+css的方法实现一个未知宽高的弹出框(或者图片)垂直水平居中??如果用JS的话就好办了,但是JS的使用 ...

  3. normalize.css介绍

    Normalize.css 只是一个很小的CSS文件,但它在默认的HTML元素样式上提供了跨浏览器的高度一致性.相比于传统的CSS reset,Normalize.css是一种现代的.为HTML5准备 ...

  4. Java集合Map接口与Map.Entry学习

    Java集合Map接口与Map.Entry学习 Map接口不是Collection接口的继承.Map接口用于维护键/值对(key/value pairs).该接口描述了从不重复的键到值的映射. (1) ...

  5. 一步步学习ASP.NET MVC3 (8)——EmptyResult,ContentResult,RedirectResult

    请注明转载地址:http://www.cnblogs.com/arhat 上一章,我们阐述了Controller,Action和ActionResult所代表的含义及使用,本章继续研究ActionRe ...

  6. 关于github在mac 10.10上无法提交代码的解决办法---备用

    接下来是正文:本文主要说明在mac 10.10版本下github无法提交代码的问题 首先如果你是一个用终端提交代码的,你可以不用看这篇文章了,这篇文章主要是用于解决github客户端提交代码的问题,此 ...

  7. white-space 属性设置如何处理元素内的空白

    定义和用法white-space 属性设置如何处理元素内的空白. 这个属性声明建立布局过程中如何处理元素中的空白符.值 pre-wrap 和 pre-line 是 CSS 2.1 中新增的. 默认值: ...

  8. PHP扩展编写示例

    1.生成描述文件,包含对函数等的定义 [chengyi@localhost php-extension]$ cat hello_cy.def string self_concat(string str ...

  9. POJ3663

    题意简单. 关键:记录每头牛的val值,每次寻找和某头牛匹配的牛时候,可以通过刚刚记录的值来计算. #include<stdio.h> #include<string.h> # ...

  10. Hibernate 一对多自身双向关联关系 用于类别表的实现

    分类:一对多自身双向关联关系 Java持久化类: package com.hyy.hibernate.one_to_many.domain; import java.util.HashSet; imp ...