Minor memory leak introduced in .NET Framework 2.0 SP1

March 20th, 2008

A while ago we received a support request from a customer that had a memory leak in his application. After showing, closing and disposing an MDI child form, the Form instance was still alive. The root path indicated that the child form instance was kept alive by the MDI parent, via the internal propertyStore field. I tried to reproduce the problem using an MDI application that was easily available: the Game of Life application used in the .NET Memory Profiler tutorials. And to my surprise the Game of Life application had a memory leak, even after the memory leak fix in the tutorial had been applied.

After having collected the last snapshot in the second .NET Memory Profiler tutorial (after closing the Game of Life MDI window), there should be no new instances of any type (since the memory leak should have been fixed). Thus if the type filter is set to only show types “With new or removed instances”, the Types list shold be empty. If the original version of .NET Framework 2.0 is used, this is almost true. A single new <GCHandle> instance will exist, but for now I will ignore this (it will be discussed in a future post).

GameOfLife snapshot (without SP)

However, testing the tutorial after having installed .NET Framework 2.0 SP1 shows completely different results (as can be seen in the picture below). Even though no additional instances have been created (Delta is 0), there are still a lot of New and Removed instances.

GameOfLife snapshot (SP1)

At the time of the last snapshot, no Game of Life windows are open, so I would expect all LifeForm instances to be GCed, but there’s still one new live instance left. So the new LifeForm instance is a good candidate for memory leak analysis. The details of the new LifeForm instance is shown in the picture below.

LifeForm instance details

The shown root path of the instance indicates that a reference to the LifeForm instance is stored in a PropertyStore. The PropertyStore class is used internally by WinForms to save space when storing property values. Instead of using instance fields, the dictionary-like PropertyStore is used. Anyway, after doing some digging around using Reflector, I came to the conclusion that the LifeForm instance was put into the PropertyStore using the Form.FormerlyActiveMdiChild property. This is a private property that seems to be only used to update the window icon when an MDI child is deactivated. Below is the code originally used to update the icon:

if( this.FormerlyActiveMdiChild != null )
{
    this.FormerlyActiveMdiChild.UpdateWindowIcon(true);
    this.FormerlyActiveMdiChild = null;
}

It seems like Microsoft tried to fix a problem with the icon update when the deactivated form was actually closing, so in .NET Framework 2.0 SP1, the code now looks like this:

if( this.FormerlyActiveMdiChild != null &&
    !this.FormerlyActiveMdiChild.IsClosing )
{
    this.FormerlyActiveMdiChild.UpdateWindowIcon(true);
    this.FormerlyActiveMdiChild = null;
}

However, this causes the assignment “this.FormerlyActiveMdiChild = null” to be skipped and a reference is left to the closed (and possibly disposed) MDI form. This prevents it (and all its child instances) from being GCed. It would probably be more correct if the original code was changed to:

if( this.FormerlyActiveMdiChild != null )
{
    if( !this.FormerlyActiveMdiChild.IsClosing )
      this.FormerlyActiveMdiChild.UpdateWindowIcon(true);
    this.FormerlyActiveMdiChild = null;
}

Note that this memory leak only causes a single form (and its child instances) to be kept in memory. If you open and close another form, the original form will become eligible for GC (i.e., the memory leak doesn’t grow over time). Nonetheless, it is preferable to make sure that all unused instances are correctly GCed. One workaround to this problem is to use reflection to set the private property “FormerlyActiveMdiChild” to null, for instance in the MdiChildActivate event in the MDI parent. Something like this:

protected override void OnMdiChildActivate( EventArgs e )
{
  base.OnMdiChildActivate( e );
  try
  {
    typeof( Form ).InvokeMember( "FormerlyActiveMdiChild",
      BindingFlags.Instance | BindingFlags.SetProperty |
      BindingFlags.NonPublic, null,
      this, new object[] { null } );
  }
  catch( Exception )
  {
    // Something went wrong. Maybe we don't have enough
    // permissions to perform this or the
    // "FormerlyActiveMdiChild" property no longer
    // exists.
  }
}

Most of the text in this post is copied from my reply to a forum question at http://memprofiler.com/forum/viewtopic.php?t=1160. In the forum I said that the problem was introduced in .NET Framework 3.5. However, installing .NET Framework 3.5 will automatically install .NET Framework 2.0 SP1 and .NET Framework 3.0 SP1, and the memory leak exists in the .NET 2.0 assembly “System.Windows.Forms.dll”.

Unfortunately, this problem affects the .NET Memory Profiler tutorials. When we get the time to update them, we will have to take this into consideration.

In my next post I will make a comment on the single <GCHandle> instance that existed after the last snapshot when using the original .NET Framework 2.0.

.NET Memory Profiler 3.1.283 Released

March 20th, 2008

Yesterday we released .NET Memory Profiler 3.1.283. This release contains a fix for a problem with memory dump imports and attached processes.

If you use the profiler to attach to a process or if you import memory dump files, it is highly recommended that you install this release.

It can be downloaded from http://memprofiler.com/download.aspx.

Several other fixes since the release of .NET Memory Profiler 3.1 are also included in this release. See the release notes for more information about all fixes.

.NET Memory Profiler 3.1 Released

February 10th, 2008

Finally. We have now released version 3.1 of .NET Memory Profiler. This version contains several new features and enhancements, such as improved memory assertions, easier navigation of snapshot data, Visual Studio 2008 support, better identification of native memory, and much more. For more information about the new release, visit the .NET Memory Profiler site at http://memprofiler.com.

You can download a fully functional trial of .NET Memory Profiler 3.1 at http://memprofiler.com/download.aspx.

.NET Memory Profiler 3.1 Beta Released

December 5th, 2007

After a lot of work with the finishing touches, we finally released the first beta of .NET Memory Profiler 3.1. This release contains several new features, such as Visual Studio 2008 support, improved presentation and navigation of snapshot data, etc. You can read more about this release and download the beta here.

We did find a few last minute problems before releasing the beta, so the release was delayed a bit. Since installing .NET Framework 3.5 caused problems with .NET Memory Profiler 3.0, we had to quickly release a maintenance release of this version (v3.0.142). So now both 3.0 and 3.1 support the latest .NET Framework release. If you’re using .NET Memory Profiler and have installed .NET Framework 3.5, I highly recommend that you download either the latest .NET Memory Profiler release, or the 3.1 beta.

Visual Studio 2008 and .NET Framework 3.5 Released

November 20th, 2007

Microsoft announced yesterday that Visual Studio 2008 has been released.

We have installed the RTM version and tested .NET Memory Profiler 3.1 against Visual Studio 2008 and .NET Framework 3.5. Everything seems to work OK so I believe that we will be able to release a beta version of .NET Memory Profiler 3.1 before the end of this week. For those that have installed .NET Framework 3.5 it is highly recommended that this beta is used, since .NET Memory Profiler 3.0 does not fully support the new framework version.

Visual Studio 2008 to be Released before the end of November

November 6th, 2007

Microsoft’s S. Somasegar recently announced that Visual Studio will ship by the end of November.

Since support for Visual Studio 2008 is not included in .NET Memory Profiler 3.0 it has been our intention to release .NET Memory Profiler 3.1 before Visual Studio 2008 was released. Unfortunately, it seems like we probably will not be able to succeed in that ambition. However, we will prepare a beta of .NET Memory Profiler 3.1,which will be released as soon as possible after Visual Studio 2008 RTM is available for download.

Memory Leak in ToolStripTextBoxControl

October 5th, 2007

Occasionally we get e-mails about potential memory leaks that a user of .NET Memory Profiler have identified using the profiler, but where they have not been able find a solution to the problem. 

What we normally do then is to request a profiler file that contains data from a session that exhibits the memory problem. Using the session file we can hopefully pinpoint what the problem is, and suggest a solution for it.

Sometimes the memory problem is related to a bug in the .NET Framework. In this post I’m going to describe one such problem and present a workaround for it.

In this case I received a session file that contained a large amount of disposed instances, which clearly indicates that there is a memory leak in the application. Without knowing too much about the application, I investigated one of the instances that were disposed. From the Types/Resources view I selected a Type that had disposed instances, and then double-clicked one of the disposed instances. Below is a root path (the only identified) for a disposed Microsoft.Reporting.WinForms.ReportViewer instance.

The root paths show that the instance are kept alive by an EventHandler (UserPreferencesChangedEventHandler), which immediately should draw your attention. A disposed instance should normally not be in use anymore, and should therefore not need to be notified about external events.

Note that this only applies to external events. For instance, if you have a form that is referenced by a child control via an event handler (e.g. TextBox.TextChanged), this is probably not causing a memory leak.

So the UserPreferencesChangedEventHandler is probably involved in this memory leak. By double-clicking the EventHandler instance (#150,722), I can see how it was created.

The EventHandler was created in the ToolStripTextBoxControl.HookStaticEvents method. Investigating ToolStripTextBoxControl using Reflector reveals the following:

  • ToolStripTextBoxControl is an internal  class that is used to put a TextBox in a ToolStrip
  • HookStaticEvents is called from OnVisibleChanged (as can be seen in the allocation stack above).
  • If the ToolStripTextBoxControl is Visible, a new event handler will be added to  SystemEvents.UserPreferencesChanged
  • If the ToolStripTextBoxControl is not Visible (or if it is being Disposed), and if an event handler has been added, the event handler will be removed from SystemEvents.UserPreferencesChanged.

The thing to be noted in the above list is that a new event handler is ALWAYS added when the Control becomes Visible, the HookStaticEvents method does not check whether the event handler has already been added (which it does before removing it). As I mentioned in my previous post, when a Control is part of a container, the OnVisibleChanged event will only be invoked when the Control becomes visible, not when it is hidden. So when the ToolStripTextBoxControl becomes hidden it will not be notified about this, but when it becomes visible again another event handler will be added. The Dispose method of ToolStripTextBoxControl will remove the event handler, but only one event handler will be removed. If the ToolStripTextBoxControl has become Visible more than once (e.g. if it’s part of a TabPage), this will not be enough, since more than one event handler will exist.

It could also be noted that removing the event handler in Dispose should not have been necessary at all, if the VisibleChanged event had been raised correctly.

In this case the ToolStripTextBoxControl was part of ToolStrip that was used by the ReportViewer, which in turn was part of a TabPage. Since the ToolStripTextBoxControl is a child of a TabPage, it is very probable that its visibility may change several times, thus creating a memory leak.

In order to fix this memory leak I wrote a small utility class (ToolStripTextBoxDisposeHelper) that tracks the number of times the event handler has been added in HookStaticEvents (by listening to the VisibleChanged event of the ToolStripTextBoxControl ). When the ToolStripTextBoxControl  is disposed  the UserPreferencesChangedEventHandler will be removed as many times as is necessary. Unfortunately this makes use of reflection, so it will not work in application that is not running under full trust.

At the end of the post you will find the code for the ToolStripTextBoxDisposeHelper class.

Using the helper class is pretty simple. After initializing the ToolStrip (e.g. after InitializeComponent has been called) a new instance of ToolStripTextBoxDisposeHelper should be created for each ToolStripTextBox item. Or the helper method CreateToolStripDisposeHelpers can be used. It will iterate over all items in the ToolStrip and create a ToolStripTextBoxDisposeHelper for each ToolStripTextBox found.

If you have experienced this memory leak I hope that this workaround is useful to you.

Finally, I just want to mention another memory leak that is related to the VisibleChanged event and ToolStrips. This memory leak occurs due to another peculiarity regarding the VisibleChanged event; when a removed control is disposed the VisibleChanged event will be raised (and the Visible property will return true). This can cause several UserPreferencesChanged event handlers to be added, which will never be removed (since the control is already disposed).

For more information about this problem, see the forum topic at:

http://memprofiler.com/forum/viewtopic.php?t=1042

At the end of the topic a short description of the problem is provided.

/// <summary>
/// Utility class for properly disposing ToolStripTextBoxes.
/// </summary>
sealed class ToolStripTextBoxDisposeHelper
{
  TextBox m_textBox;
  int m_visibleCount;          

  /// <summary>
  /// Helper method for creating a ToolStripTextBoxDisposeHelper
  /// for erach ToolStripTextBox in a ToolStrip.
  /// </summary>
  /// <param name="toolStrip">A ToolStrip containing ToolStripTextBoxes
  /// that should be tracked</param>
  public static void CreateToolStripDisposeHelpers( ToolStrip toolStrip )
  {
    foreach( ToolStripItem item in toolStrip.Items )
    {
      ToolStripTextBox toolStripTextBox = item as ToolStripTextBox;
      if( toolStripTextBox != null )
      {
        new ToolStripTextBoxDisposeHelper( toolStripTextBox );
      }
    }
  }          

  /// <summary>
  /// Initializes a new ToolStripTextBoxDisposeHelper.
  /// </summary>
  /// <remarks>
  /// Creating a new ToolStripTextBoxDisposeHelper will track
  /// visibility of the provided ToolStripTextBox and make sure
  /// that it is correctly disposed. There is no
  /// need to store a reference to the created instance. The
  /// helper will be kept alive by event handlers in the
  /// ToolStripTextBox.
  /// </remarks>
  /// <param name="textBox">The ToolStripTextBox to track.</param>
  public ToolStripTextBoxDisposeHelper( ToolStripTextBox textBox )
  {
    m_textBox = textBox.TextBox;
    m_textBox.VisibleChanged += new EventHandler( textBox_VisibleChanged );
    m_textBox.Disposed += new EventHandler( textBox_Disposed );
  }          

  void textBox_Disposed( object sender, EventArgs e )
  {
    // Remove all but one event handlers, since
    // the last one will be removed in
    // ToolStripTextBox.ToolStripTextBoxControl.Dispose.
    try
    {
      UserPreferenceChangedEventHandler eventHandler =
        (UserPreferenceChangedEventHandler)Delegate.CreateDelegate(
          typeof( UserPreferenceChangedEventHandler ),
          m_textBox, "OnUserPreferenceChanged" );
      for( int i = 0; i < m_visibleCount - 1; i++ )
      {
        SystemEvents.UserPreferenceChanged -= eventHandler;
      }
    }
    catch( MissingMethodException )
    {
      // The UserPreferencesChanged implementation in the framework has
      // changed. Hopefully this memory leak has been fixed and we can
      // ignore that this hack didn't succeed.
    }
    catch( MemberAccessException )
    {
      // We don't have permissions to fix this problem using reflection.
      // Handle this somehow (currently we just rethrow the exception)
      throw;
    }
  }          

  /// <summary>
  /// Count the number of times the TextBox has become visible
  /// (according to VisibleChanged)
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  void textBox_VisibleChanged( object sender, EventArgs e )
  {
    if( m_textBox.Visible )
      m_visibleCount++;
    else
      m_visibleCount--;
  }
}

The Control.VisibleChanged event

September 19th, 2007

I thought I should write something about some memory leaks that I have recently discovered in .NET Framework 2.0 (when helping .NET Memory Profiler customers). But since the memory leaks are caused by another problem in the framework - the Control.VisibleChanged event - I’ll start by writing something about this.

In 2001 we were working on converting our Lab Assistant project from Visual J++ to .NET (this was pre-.NET 1.0, but Visual J++ was being abandoned). Lab Assistant is an application that connects to and controls scientific instruments over the network. One requirement in Lab Assistant is that data collected from the instruments should be presented in real-time in the user interface application, and the UI should react to events (e.g. state changes) generated by the instruments. In order to implement this the user interface needs to retrieve the data to present, subscribe to remote events etc. However, a lot of different controls are used to present different types of data, and all controls will not be visible at any one time. The controls can for instance be part of a TabPage that is not being shown. Subscribing to remote events and retrieving remote data for controls that are not shown is unnecessary, can affect the performance and can cause increased network traffic. There is an easy solution to this - only retrieve data and listen to remote events while the Control viewing/editing the data is visible. So now we just needed to know when the Control becomes visible or invisible, which should be simple enough since the Control class provides a VisibleChanged event.

Unfortunately we quickly realized that the VisibleChanged event didn’t work as we expected. The Control.Visible property is a pretty special property. The documentation says:

Gets or sets a value indicating whether the control is displayed.

However, setting the Visible property to true will not cause a child control to be displayed if the parent container is invisible. This is very understandable, because that would force all parent containers to become visible as well, which would be an unexpected (and most likely unwanted) side-effect. So setting the Visible property to true means: I want this control to be displayed if (or when) all containers in the Control hierarchy are also displayed. On the other hand, the documentation is correct regarding the getter. It will only return true if all containers are also visible, thus the getter and the setter are not referring to the same concept. A better solution would be to make the Visible property read-only, and have some other property for setting/getting the wanted visibility of a specific control (this is how it is implemented in WPF, where you have a Visibility get/set property and an IsVisible read-only property).

Well, back to the issue with the VisibleChanged event. Since we’re only interested in whether the Control is actually displayed or not, getting the Visible property provides us with the information we want. We just need to be notified when the property changes. Consider the following code snippet:

panel = new Panel();
panel.VisibleChanged += new EventHandler( panel_VisibleChanged );
label = new Label();
label.VisibleChanged += new EventHandler( label_VisibleChanged );
panel.Controls.Add( label );

Assuming that panel.Visible = true and label.Visible = true, then setting:

panel.Visible = false;

will raise the panel_VisibleChanged event and panel.Visible will be false and label.Visible will be false (since the container is not visible). The important thing to note here is that the label_VisibleChanged event was NOT raised even though label.Visible changed from true to false.

Setting:

panel.Visible = true;

will raise BOTH panel_VisibleChanged and label_VisibleChanged (and both panel.Visible and label.Visible will be true again).

So, when changing the Visible settings on a container Control, the VisibleChanged event for child controls will only be raised when the child becomes visible, not when it’s hidden. This renders the VisibleChanged event pretty useless for most uses that I can think of.

Investigating the Control.Visible property using Reflector (or ILDasm as I used at the time)  reveals what’s causing the problem. When setting the Visible property, an internal visibility state flag is set or cleared, depending on the property value. After setting the state flag, the Control calls OnVisibleChanged, which raises the VisibleChanged event and propagates the change to child controls (via OnParentVisibleChanged), but only if the child control is Visible. This check is performed because of the fact that a child control that is not Visible, will not be affected by the visibility of the parent control.  The problem is that the child control has already become invisible at this time, so the change is never propagated. Instead of checking the Visible property when propagating, the internal state flag should have been used. This is actually what happens in the OnParentVisibleChanged method. It checks whether the internal visibility flag is set, and raises the VisibleChanged event if it is, so the pre-checking before calling OnParentVisibleChanged is redundant and erroneous.

I have reported about this issue a few times in a Microsoft newgroup (the first time was before .NET 1.0 was released), but the problem still exists in .NET 2.0. It is not a serious problem, and fixing it might cause problems in programs that somehow rely on this strange behaviour.

We have written a workaround for the problem, so I have not thought about the problem for a while. Not until I helped a .NET Memory Profiler customer that had a problem with a memory leak. This leak appeared to be (at least partially) caused by the VisibleChanged problem described in this post. In my next post I will provide some information about this memory leak.

.NET Memory Profiler 3.0.141 Released

August 19th, 2007

We recently released a maintenance release for .NET Memory Profiler 3.0 - v3.0.141.

It can be downloaded from http://memprofiler.com/download.aspx.

This release contains fixes for several problems, both minor UI fixes and other more serious issues. See the release notes for more information about whats new in this release.

Unrelated to this release, I must say that so far I have not written blog posts nearly as regularly as I planned. I have some topics in mind, so I hope that I will find the time to do some blog writing.

.NET Memory Profiler 3.0.113 Released

May 6th, 2007

We recently released a minor maintenance release for .NET Memory Profiler 3.0 - v3.0.113.

It can be downloaded from http://memprofiler.com/download.aspx.

This release only contains a single fix, related to profiling WPF applications under a 64-bit OS. For some reason the dispose tracker caused an error when injecting code in a WPF assembly. And this error only occured when running under a 64-bit OS. Luckily, we had previously written another “code injector” which used another approach for code injection. I believe the implementation of the new “injector” is better, but we never included it, since the old one seemed to work OK (don’t fix it unless it’s broken?), and we wanted to perform some additional testing before replacing. Anyway, now we have replaced it and everything seems to be working OK; even for WPF applications under 64-bit Windows.