ListView may leak with ImageLists

Today, I encountered a problem with WinForm’s ListView imagelist. When you assign a global imagelist to the listview, then the listview will remain in memory forever. When the ListView references other objects, then these objects won’t be collected too and you might end up with a huge memory leak.

When does this occur?
ListViews won’t be collected until the associated imagelist(s) is collected, when you don’t reset the SmallImageList, LargeImageList or StateImageList properties yourself.

Why does this occur?
The LargeImageList property is implemented like this:

public ImageList LargeImageList
{
 get { return this.imageListLarge; }
 set
 {
  if (value != this.imageListLarge)
  {
   // Unsubscribe from the old image list events
   if (this.imageListLarge != null)
   {
    this.imageListLarge.RecreateHandle -= LargeImageListRecreateHandle;
    this.imageListLarge.Disposed -= DetachImageList;
    this.imageListLarge.ChangeHandle -= LargeImageListChangedHandle;
   }

   // Save new imagelist
   this.imageListLarge = value;

   // Subscribe to the new image list events
   if (value != null)
   {
    value.RecreateHandle += new EventHandler(LargeImageListRecreateHandle);
    value.Disposed += new EventHandler(DetachImageList);
    value.ChangeHandle += new EventHandler(LargeImageListChangedHandle);
   }

   // Update the underlying imagelist,
   // if the control is already created
   if (base.IsHandleCreated)
   {
    IntPtr imageListHandle = (value==null)?IntPtr.Zero:value.Handle;
    base.SendMessage(LVM_SETIMAGELIST, IntPtr.Zero, imageListHandle);
    if (this.AutoArrange && !this.listViewState[4])
     this.UpdateListViewItemsLocations();
   }
  }
 }
}

The implementation of SmallImageList and StateImageList are similar. You can clearly see that the ListView subscribes to the following three events of the ListView:

  • The RecreateHandle event is raised when the ImageList handle is recreated, because some of the properties have changed, that require that the underlying ImageList object to be recreated. The ListView responds to this event by setting the new ImageList to the underlying ListView object.
  • The Disposed event is raised when the ImageList is disposed. The ListView responds to this event by resetting the LargeImageList property to NULL.
  • The ChangeHandle event is an internal (and undocumented) event from the ImageList that is raised, when the ImageList is changed (i.e. adding or removing an image from the imagelist). The ListView responds to this event by setting the item images. Note that this event is only subscribed to for the LargeImageList property.

The basic idea of the garbage collector is that objects that aren’t referenced anymore are collected. As long as you reference an object it will stay alive. Subscribing to an object means that you reference the object via the event handler delegate. So, before the ListView can be collected, the reference to the imagelists should be removed by setting the properties to NULL. This will effectively unsubscribe from the three events.

When the ListView is disposed, then it should make sure that it unsubscribe from these events to prevent that the subscription keeps the ListView alive. The Dispose method is implemented like this:

protected override void Dispose(bool disposing)
{
 if (disposing)
 {
  // Reset small imagelist
  if (this.imageListSmall != null)
  {
   this.imageListSmall.Disposed -= DetachImageList;
   this.imageListSmall = null;
  }

  // Reset large imagelist
  if (this.imageListLarge != null)
  {
   this.imageListLarge.Disposed -= DetachImageList;
   this.imageListLarge = null;
  }

  // Reset state imagelist
  if (this.imageListState != null)
  {
   this.imageListState.Disposed -= DetachImageList;
   this.imageListState = null;
  }

  // [some other code removed]
  // ...

  // Call base class
  base.Dispose(disposing);
 }
}

As you can see, this method only unsubscribes from the Disposed event, but not from the imagelist’s RecreateHandle and ChangeHandle events. The garbage collector will keep the ListView alive, until the associated imagelists are collected. When you use a global imagelist, then this will never occur and each instance of the ListView that uses the imagelist will remain alive forever.

Microsoft can easily fix this problem by rewriting the Dispose method like this:

protected override void Dispose(bool disposing)
{
 if (disposing)
 {
  // Reset imagelists (note that we set the property, not the attribute)
  this.ImageListSmall = null;
  this.ImageListLarge = null;
  this.ImageListState = null;

  // [some other code removed]
  // ...

  // Call base class
  base.Dispose(disposing);
 }
}

For now the only thing that you can do is to make sure you reset the imagelist yourself before your ListView is disposed.

So how about TreeViews?
A TreeView also uses an imagelist, so it would make sense that the same problem exists there. Fortunately, the TreeView is fine, because the implementation uses two special methods to subscribe and unsubscribe from the three events. The Dispose method of the TreeView calls the unsubscribe method, so the TreeView is fine.

Sample application
I have created a sample application that clearly demonstrates the problem. I needed to override the ListView class to make sure I could count its instances, but it doesn’t have any other side effects.

To illustrate the problem, start the application and press the “Create form” button. This will create a ListView and populates it with some items. When you refresh the counters you’ll see that you have one instance and one active instance (this means an instance that hasn’t been disposed yet). If you close the form and refresh the counters again, then you’ll see that there is no active instance anymore, but this is still a ListView instance. This means that the form has been disposed, but hasn’t been collected. You can try this over and over, but you will never see the number of ListView instances drop.

When you enable the “Reset imagelists when disposing” checkbox in the main form, then the properties are reset and you’ll see that the number of instances will stay in sync with the number of active instances.
Download the sample application.

Response from Microsoft
I have submitted this bug into the MSDN Managed Newsgroups and the Microsoft Feedback website. Microsoft acknowledges this to be a problem and, when it fits the schedule, it will be fixed. The recently released .NET Framework 4 Beta 1 doesn’t include the fix.

Microsoft updated the status to resolved (closed) on June 7th, 2009, but didn’t provide additional information how and when the fix will be available.

Memory leak with WPF resources (in rare cases)

It seems there is bug in the WPF resource architecture that can cause memory not to be released and causing performance and stability problems. The document describes when the problem occurs and why.

During the development of a WPF application, we noticed that a huge amount of memory was used after a while. After profiling with the excellent .NET Memory Profiler v3.1 we found out that a lot of objects were not collected by the garbage collector. After some heavy investigation I found out that a DropShadowEffect was holding references to objects that should have been garbage collected already.

When does the problem occur?
The problem occurs in the following situation:

  1. A style is defined in the application’s ResourceDictionary.
  2. The style uses a control template that uses media effects (i.e. DropShadowEffect).
  3. The media effect should be referenced using a StaticResource.

Why does the problem occur?
When a media effect is attached to a UIElement, then the UIElement.VisualEffectInternal property setter is invoked by the WPF framework. This method registers to the Effect.Changed event when the media effect is not frozen. This is correct, because a non-frozen effect might change its properties and the UI element should invalidate. A frozen effect cannot be changed, so there is no need to register to the Effect.Changed event.

This behavior is correct and also documented in the MSDN library in the chapter Optimizing Performance: Object Behavior:

Changed Handlers on Unfrozen Freezables may Keep Objects Alive
The delegate that an object passes to a Freezable object’s Changed event is effectively a reference to that object. Therefore, Changed event handlers can keep objects alive longer than expected. When performing clean up of an object that has registered to listen to a Freezable object’s Changed event, it is essential to remove that delegate before releasing the object.

WPF also hooks up Changed events internally. For example, all dependency properties which take Freezable as a value will listen to Changed events automatically.

Unfortunately, there seems to be no code in the WPF framework that removes the delegate again. The only way to unsubscribe is to set the Effect to NULL. Using media effects that are not frozen seems to be dangerous.

As long as the media effect is frozen, then the framework doesn’t subscribe to the Changed event and everything is fine. But that’s the problem… When using a merged resource dictionary at the application level, then the resources inside of it are not always frozen (see the example application). If the resource is marked explicitly to be frozen, then it will be frozen and everything works fine.

What kind of puzzled me, is the fact that when the effect and style are defined directly at the “Application.Resources” level (without a resource dictionary), then the effect is frozen by default. If they are defined in a resource dictionary that is merged inside the “Application.Resources” then the effect is not frozen! You can force the effect to be frozen by specifying “PresentationOptions:Freeze=True”, but this is not common behavior. So, putting resources into resource dictionaries has side effects that are not foreseen and are not (clearly) documented in the WPF documentation.

Because the media effect is shared and the delegates are not removed when the UI element is not needed anymore it keeps the UI elements alive. WPF holds a strong hierarchy in objects, so a lot of other objects stay in memory too, resulting in a lot of wasted memory that can never be regained anymore.

The actual problem is that effects that are defined in a resource dictionary that is loaded from the Application object are not frozen in some cases. I haven’t done a thorough investigation why this doesn’t happen, but I think this is a bug in the WPF framework. The framework has a lot of places where it checks if a resource is of a certain type and if it is, then it should be sealed or frozen. I wouldn’t be surprised that the framework misses such a check for media effects, because they have been added later (in v3.5 SP1). Of course, this statement is pure speculation.

The workaround is simple… Just add the Freeze attribute to all the effects that you don’t plan to modify at runtime.

Sample application
I have created a sample application that illustrates the problem. When the application is started, then you can see a checkbox that has a dropshadow effect applied to it. The window also shows whether or not this effect is frozen.

As you can see the effect is not frozen, when the effect and style are defined in the resource dictionary. If you comment out the resource dictionaries from App.xaml and uncomment the effect and style resources in App.xaml, then the application looks just the same, but you’ll see that the effect is now frozen.

The application doesn’t illustrate the actual memory leak, but in a real-life application (like ours) it actually has memory leaks that can become quite large.

Conclusion
I guess Microsoft introduced a problem when using effect and styles in resource dictionaries. Because I didn’t investigate why it wasn’t frozen, I cannot determine whether this is a real bug or just a side-effect of some design choice.

I also understand that it might be a bad idea to define resources at the application level this way. Using themes is probably a better solution and might solve this issue. But, there are just people out there using this approach and they don’t expect this behavior. The fix is simple… Just freeze all media effects that you don’t plan to change at runtime, which helps us to fix this for our next release.

Sample application
Download the sample application.

Memory leak with WPF animations

It seems there is bug in the WPF animation architecture that can cause memory not to be released and causing performance and stability problems. The document describes when the problem occurs and why.

During the development of a WPF application, we noticed that a huge amount of memory was used after a while. After profiling with the excellent .NET Memory Profiler v3.1 we found out that a lot of objects were not collected by the garbage collector.

After some heavy investigation I found out that a FrameworkTemplate was holding references to a lot of my textboxes that should have been collected already. These textboxes held references to other objects, so the result was that a lot of memory was wasted. When the event triggers with actions were removed from the textbox’s control template, then the memory was released properly.

When does the problem occur?
The problem occurs in the following situation:

  1. The control has a control template defined.
  2. The control template uses triggers with enter/leave actions.
  3. The control has the ‘Collapsed’ visibility.

Why does the problem occur?
Triggers are active during the initialization of a control. If a property triggers an enter/leave action, then the action is started. The action is started via the internal “StyleHelper.InvokeActions” method. This method checks if the visual tree of the object is already constructed and if it is, then the actions are invoked immediately.

If the visual tree hasn’t been created yet, then the action is queued by the “StyleHelper.DeferActions”, so it can be processed later. To my surprise, the deferred actions are stored with the template and not the control itself (IMHO: This is a fundamental design flaw). If the visual tree is created, then the deferred actions will be invoked.

The visual tree is created during the public “FrameworkElement.ApplyTemplate” method. The framework only calls this method when measuring the control. So if the control is never measured, then the visual tree is not created and the deferred actions will remain queued forever.

The MSDN documentation for “FrameworkElement.MeasureOverride” clearly states:

Important Note:
Elements should call Measure on each child during this process, otherwise the child elements will not be correctly sized or arranged.

You might expect that all elements call “FrameworkElement.Measure” on their children, so during this process the “FrameworkElement.ApplyTemplate” is called. However, WPF doesn’t respect this rule, because “UIElement.Measure” doesn’t start measuring when the element has a collapsed visibility.

Collapsed UI elements are never measured and if they never will get the Visible or Hidden visibility, then the object is never measured and the template isn’t applied. This seems very logical, but it also causes the deferred actions never to be removed from the template.

Because the template keeps the reference to the control, the control is never garbage collected.  WPF controls know their parents and children. They also reference other objects via databinding. The result is that a lot of objects are referenced by the control (direct or indirect) and are not garbage collected.

Sample application
I have created a sample application that illustrates the problem. When the application is started, then you can see the deferred actions for the control template of the MySampleControl.

When you press the “Create Collapsed Expander”, the application creates a window that contains a collapsed expander that contains a MySampleControl. Because the MySampleControl is collapsed it isn’t constructed and you can see the control is added to the deferred actions list (after refreshing the list).

When you expand the expander on the window and refresh the deferred actions, then you can see that the control is removed from the list. If you close the window, without expanding the expander, then the control will stay on the list forever. Because the item is still on the list it is never collected.

You can force a garbage collection and in the debug window (when running in Visual Studio) you can see which objects are finalized. Windows with expanders that have never been opened will never be finalized.

Conclusion
I guess Microsoft made a design error by storing the deferred actions with the template. They should have been stored in the control itself. The deferred actions are only used when applying the template, which is an instance method of the control. So it would have had full access. Now a dictionary is used that connects objects that shouldn’t be connected.

Response from Microsoft
I have received a response from Microsoft and they confirm it is a bug. It is on the list to be fixed, but there is no releasedate for .NET 3.5 SP2 or .NET 4. So I guess we’ll have to wait.

Microsoft .NET Framework 4
Beta 1 of the Microsoft .NET Framework 4 has been released and it now uses a ConditionalWeakTable instead of a HybridDictionary. The keys are kept as weak references and when the key is collected by the GC, then the associated value is also released. This also solves this issue in our situation, because the dictionary doesn’t keep the objects alive anymore.

Sample application
Download the sample application.