Top common Memory leaks using C#
By: Nikita Starichenko
Tags: Programming, Leak Detection, Dotnet, Software Development, C Sharp Programming

Despite the presence of GC, it is very easy to cause memory leaks. It’s not that the garbage collector works bad, it’s just that there are too many ways to cause memory leaks in a managed language.

In this article, we will cover the most common reasons for memory leaks in .NET programs. All examples are in C#.

Here we start:

1. Сlosure

A leak occurs when a member of a class is captured by an anonymous method through a closure.

Example:

In this code, the _id field is captured in the anonymous method and the instance is referenced as well. This means that while Worker exists and references job delegate in Do method, it will also reference an instance of Closure .

The solution can be simple — using a local variable instead of class member:

By using the value to a local variable, nothing is captured and you’ve prevented a potential memory leak.

2. Events

.NET events can cause memory leaks. When you subscribe to an event, the event triggering object contains a reference to your class. If the subscription occurs through an anonymous method, then this does not happen.

Example:

If eventManager object outlives EventLeak class, you have a memory leak. Any instance of EventLeak is referenced by eventManager and will not be collected by GC.

How to avoid:

Unsubscribe from the event. Use weak-handler patterns. Subscribe with an anonymous function and without capturing any members.

3. Static

Static objects live at the GC Root and are never collected by the collector. That behavior leads to memory leak. Static objects and everything they reference will never be garbage collected.

Example:

Any instance of Static will forever stay in memory, causing a memory leak.

4. Cache

If you cache everything, you will eventually run out of memory.

Example:

This code can save your database, but will fill all your available memory.

Some ways to solve this:

Delete caching that wasn’t used for some time Limit caching size Use WeakReference to hold cached objects. (WekReference)

5. Threads

If you create an infinitely-running thread that does nothing and has references to objects, that would be a memory leak. One example of how this can easily happen is with a Timer .

Example:

If you don’t stop the timer, it will run in a separate thread, referencing an instance of Threads , preventing it from being collected.

6. IDisposable

Incorrect usage of IDisposable pattern can cause a memory leak.

How to properly call Dispose:

One thing you can do is to use the using statement in C#:

This works on IDisposable classes and translates by the compiler to this:

This is very useful because even if an exception was thrown, Dispose will still be called.

Another thing you can do is utilize the Dispose Pattern. Here’s an example how to impement it:

This pattern makes sure that even if Dispose wasn’t called, then it will eventually be called when the instance is garbage collected. If, on the other hand, Dispose was called, then the finalizer is suppressed. Suppressing the finalizer is important because finalizers are expensive and can cause performance issues.

7. WPF Bindings

WPF Bindings can cause memory leaks. The rule of thumb is to always bind to a DependencyObject or to a INotifyPropertyChanged object. When you don’t do it, WPF will create a strong reference to your binding source (ViewModel) from a static variable, causing a memory leak (explanation).

Here’s an example:

This View Model will stay in memory forever:

Whereas this View Model won’t cause a memory leak:

It doesn’t matter if you invoke PropertyChanged or not, the important thing is that the class derives from INotifyPropertyChanged . This tells the WPF infrastructure not to create a strong reference.

The memory leak occurs when the binding mode is OneWay or TwoWay. If the binding is OneTime or OneWayToSource, it’s not a problem.

Another WPF memory leak issue occurs when you bind to a collection. If that collection doesn’t implement INotifyCollectionChanged , then you will have a memory leak. You can avoid the problem by using ObservableCollection which does implement that interface.

8. Unmanaged memory

Unmanaged memory is a memory that isn’tmanaged by the garbage collector. And you need to deallocate the memory by yourself.

Example:

In the above method, we’ve used the Marshal.AllocHGlobal , which allocates a buffer of unmanaged memory (documentation). Without manually releasing the handle with Marshal.FreeHGlobal , that buffer memory will be taken in the process`s memory heap, causing a memory leak.

To deal with such problems you can add a Dispose method that frees any unmanaged resources, like so:

Unmanaged memory leaks can cause memory fragmentation.