none
Memory Leak - Trouble figuring out with windbg.exe what causes the memory leak

    Question

  • I'm developing a windows forms application and I'm using the december 2005 CAB library. In my main form I have a DeckWorkSpace in which a view is loaded after a CAB command is triggered by selecting a menu item of the main Form's MenuStrip. After I terminate the workitem (WorkItem.Terminate()) which loaded the view in the DeckWorkSpace I force a garbage collection. I then use windbg to check if the view (UnreadMsgAppsView) is still on the managed heap and this is the case.

    I realy can't figure out what exactly is keeping the view into memory. Underneath you'll find an extract of the !gcroot command for the view. Can anybody tell me what the System.Collections.Hashtable+bucket[] exactly is and how I should interprete this output from gcroot. (in the graphs of each scanned thread only weak references appear, so no strong or pinned references). Thank you very much for your help.

     

    0:015> !gcroot 01423e44
    Note: Roots found on stacks may be false positives. Run "!help gcroot" for
    more info.
    ebx:Root:01293b5c(System.Windows.Forms.Application+ThreadContext)->
    012ab258(Memo.TeleSec.FormMain)->
    012ccd74(System.ComponentModel.EventHandlerList)->
    013b3144(System.ComponentModel.EventHandlerList+ListEntry)->
    01453ff0(System.EventHandler)->
    0142c9bc(System.Object[])->
    01453fd0(System.EventHandler)->
    01420b98(System.Windows.Forms.ToolTip)->
    01420c0c(System.Collections.Hashtable)->
    01420c44(System.Collections.Hashtable+bucket[])->
    0142093c(Memo.TeleSec.UI.Common.Buttons.HideSidePanelButton)->
    014217b4(System.EventHandler)->
    0141f394(Memo.TeleSec.UI.ReportsModule.Reports.ReportsView)->
    01420648(QualiBits.Windows.Forms.ClickableTreeView)->
    01423d60(System.EventHandler`1[[QualiBits.DataEventArgs`1[[System.Windows.Forms.TreeNode, System.Windows.Forms]], QualiBits]])->
    01423d48(System.Object[])->
    01423d28(System.EventHandler`1[[QualiBits.DataEventArgs`1[[System.Windows.Forms.TreeNode, System.Windows.Forms]], QualiBits]])->
    01423cc0(QualiBits.Windows.Forms.ClickableTreeNode)->
    01423dc8(System.EventHandler)->
    014232a0(Memo.TeleSec.UI.ReportsModule.Reports.UnreadMsgAppsWorkItem)->
    01423e44(Memo.TeleSec.UI.ReportsModule.Reports.UnreadMsgAppsView)
    Scan Thread 0 OSTHread 750
    Scan Thread 2 OSTHread 1264
    Scan Thread 6 OSTHread 1590
    Scan Thread 7 OSTHread 16d8

    <Here only weak references appear>

    Tuesday, December 18, 2007 1:04 PM

Answers

  • bucket[] is a private member of HashTable class, bucket is an private struct type of the class. You can get this information using .NET Reflector.

    Basically, Root is the thread context, root hold FormMain, FormMain
    hold EventHandlerList ....
    ToolTip hode Hashtable, HashTable hold bucket[]....

    "->" means one object before it hold another object next to it in the next line.

    Thanks!
    Thursday, December 20, 2007 9:21 AM

All replies

  • bucket[] is a private member of HashTable class, bucket is an private struct type of the class. You can get this information using .NET Reflector.

    Basically, Root is the thread context, root hold FormMain, FormMain
    hold EventHandlerList ....
    ToolTip hode Hashtable, HashTable hold bucket[]....

    "->" means one object before it hold another object next to it in the next line.

    Thanks!
    Thursday, December 20, 2007 9:21 AM
  • Thanks for the info.

     

    But how is it possible that a tooltip keeps an object alive even after garbage collection. If you look at the graph, you'll find that the HideSidePanelButton holds a tooltip. The HideSidePanelButton derives from a class which on it's turn derives from button as you can see in the code snippet below. Why is my object kept alive by the tooltip? Isn't it disposed when the UserControl on which the HideSidePanelButton resides is disposed and the garbage collector has run?

     

    Code Snippet

    public class HideSidePanelButton : ImageButton

    {

       public HideSidePanelButton()

       : base()

       {

          base.ToolTip = Properties.Resources.StrMinimize;

          base.Text = "";

          base.ToggleControl = true;

          base.ImageList = ImageLists.HideSidePanelButton;

          base.Width = 21;

          base.Height = 13;

       }

     

       protected override void OnCheckedChanged()

       {

          base.OnCheckedChanged();

          if ( base.Checked )

          {

             base.ToolTip = Properties.Resources.StrMaximize;

          }

          else

          {

             base.ToolTip = Properties.Resources.StrMinimize;

          }

       }

    }

     

     

    [ToolboxBitmap( typeof( System.Windows.Forms.Button ) )]

    public class ImageButton : Button

    {

       private ToolTip mButtonToolTip;

       ...

     

       private void InitializeComponent()

       {

          components = new Container();

          ...

          mButtonToolTip = new ToolTip();

          mButtonToolTip.AutoPopDelay = 5000;

          mButtonToolTip.InitialDelay = 300;

          mButtonToolTip.ReshowDelay = 500;

          mButtonToolTip.UseAnimation = false;

          mButtonToolTip.ShowAlways = true;

          ...

       }

     

       public string ToolTip

       {

          get

          {

             return mToolTip;

          }

          set

          {

             mToolTip = value;

             mButtonToolTip.SetToolTip( this, value );

          }

       }

    ...

    }

     

     

    Thank you very much for your help.

    Kind regards.

     

     

    Thursday, December 20, 2007 9:56 AM
  • OK, I had to pass the components object in the constructor of the ToolTip. This I did and I also set the ImageList of the control to null in the ImageButton's dispose() method but when I than check if the HideSidePanelButton is still present on the managed heap I get the following output from windbg.exe (see below). As you can see, There is still an object HideSidePanelButton on the managed heap allthough gc.Collect() is executed. I don't see any strong or pinned references to the object so why is it still on the managed heap?

     

    Thanks!

     

    0:015> !dumpheap -stat -type HideSidePanelButton
    total 1 objects
    Statistics:
          MT    Count    TotalSize Class Name
    04667b5c        1          228 Memo.TeleSec.UI.Common.Buttons.HideSidePanelButton
    Total 1 objects
    0:015> !dumpheap -mt 04667b5c       
     Address       MT     Size
    014cc4cc 04667b5c      228    
    total 1 objects
    Statistics:
          MT    Count    TotalSize Class Name
    04667b5c        1          228 Memo.TeleSec.UI.Common.Buttons.HideSidePanelButton
    Total 1 objects
    0:015> !gcroot 014cc4cc
    Note: Roots found on stacks may be false positives. Run "!help gcroot" for
    more info.
    Scan Thread 0 OSTHread 99c
    Scan Thread 2 OSTHread 165c
    Scan Thread 6 OSTHread 10e4
    Scan Thread 7 OSTHread 10bc
    Scan Thread 10 OSTHread 162c
    Scan Thread 4 OSTHread 2e8
    DOMAIN(0015BCB8):HANDLE(WeakLn):3f288c:Root:014d5924(System.Windows.Forms.NativeMethods+WndProc)->
    014d1e1c(System.Windows.Forms.Control+ControlNativeWindow)->
    014d1d88(System.Windows.Forms.VScrollBar)->
    014d1e90(System.ComponentModel.EventHandlerList)->
    014d21fc(System.ComponentModel.EventHandlerList+ListEntry)->
    014d21e8(System.ComponentModel.EventHandlerList+ListEntry)->
    014d21d4(System.ComponentModel.EventHandlerList+ListEntry)->
    014d21b4(System.Windows.Forms.ScrollEventHandler)->
    014d1b4c(Memo.TeleSec.UI.Common.EditControls.MemoDataGridView)->
    014d21a4(System.ComponentModel.EventHandlerList)->
    014d37a8(System.ComponentModel.EventHandlerList+ListEntry)->
    014d3788(System.Windows.Forms.DataGridViewCellEventHandler)->
    014cfe50(Memo.TeleSec.UI.ReportsModule.Reports.UnreadMsgAppsView)->
    014d4488(Memo.TeleSec.UI.Common.Modules.IntegratedClickableTreeNodeControllerBase)->
    014cf3f4(Memo.TeleSec.UI.ReportsModule.Reports.UnreadMsgAppsWorkItem)->
    014cabbc(Memo.TeleSec.UI.ReportsModule.Reports.ReportsWorkItem)->
    014cb1e8(Memo.TeleSec.UI.ReportsModule.Reports.ReportsView)->
    014cc4cc(Memo.TeleSec.UI.Common.Buttons.HideSidePanelButton)
    DOMAIN(0015BCB8):HANDLE(WeakLn):3f28b0:Root:014d5e74(System.Windows.Forms.NativeMethods+WndProc)->
    014cb7f8(System.Windows.Forms.Control+ControlNativeWindow)->
    014cb724(QualiBits.Windows.Forms.FlickerFreePanel)->
    014cb8d4(System.ComponentModel.EventHandlerList)->
    014cd05c(System.ComponentModel.EventHandlerList+ListEntry)->
    014cd03c(System.EventHandler)->
    014cb1e8(Memo.TeleSec.UI.ReportsModule.Reports.ReportsView)
    DOMAIN(0015BCB8):HANDLE(WeakLn):3f28b4:Root:014d5e94(System.Windows.Forms.NativeMethods+WndProc)->
    014cb9b4(System.Windows.Forms.Control+ControlNativeWindow)->
    014cb910(QualiBits.Windows.Forms.OrientedTextLabel)->
    014cba28(System.ComponentModel.EventHandlerList)->
    014cd0a8(System.ComponentModel.EventHandlerList+ListEntry)->
    014cd088(System.EventHandler)->
    014cb1e8(Memo.TeleSec.UI.ReportsModule.Reports.ReportsView)
    DOMAIN(0015BCB8):HANDLE(WeakLn):3f28b8:Root:014d5eb4(System.Windows.Forms.NativeMethods+WndProc)->
    014cc5d8(System.Windows.Forms.Control+ControlNativeWindow)->
    014cc4cc(Memo.TeleSec.UI.Common.Buttons.HideSidePanelButton)
    DOMAIN(0015BCB8):HANDLE(WeakLn):3f2b20:Root:014d49e0(System.Windows.Forms.NativeMethods+WndProc)->
    014cc3b8(System.Windows.Forms.Control+ControlNativeWindow)->
    014cc1c8(QualiBits.Windows.Forms.ClickableTreeView)->
    014d7a30(System.EventHandler`1[[QualiBits.DataEventArgs`1[[System.Windows.Forms.TreeNode, System.Windows.Forms]], QualiBits]])->
    014d7310(System.Object[])->
    014cd8b8(System.EventHandler`1[[QualiBits.DataEventArgs`1[[System.Windows.Forms.TreeNode, System.Windows.Forms]], QualiBits]])->
    014cb1e8(Memo.TeleSec.UI.ReportsModule.Reports.ReportsView)
    DOMAIN(0015BCB8):HANDLE(WeakLn):3f2b2c:Root:014d47f8(System.Windows.Forms.NativeMethods+WndProc)->
    014cb328(System.Windows.Forms.Control+ControlNativeWindow)->
    014cb1e8(Memo.TeleSec.UI.ReportsModule.Reports.ReportsView)

    Friday, December 21, 2007 4:29 PM
  • In a .net windows application the form might have resource leaks though it is running with managed codes. We can use the below procedure to check if a form is having resource leak.
    1. Open  Windows Task Manager
    2. Click on Process tab.
    3. Select "View" in the menu and then select "Select Columns" menu item.
    4. Check the USER Objects and GDI Objects (check boxes) to make them appear on the process page list header.
    5. The code in your project that lunches the Win Form, please ensure you have called the Dispose method. Forms implement the IDisposable interface so their dispose method must be called the moment they are no longer needed (Test 1). We can call Dispose explicitly or even better to instantiate it implicitly by the help of using clause (Test 2).
    6. We can use the GC.Collect() after the using statement or after the call to dispose for troubleshooting purpose.
    7. Now time to launch the form. Please note the note the USER Objects and GDI Objects values at the task manager. Close the form after some time and when the form is closed, note the values again at the task manager. We can find the value is decreased if it is increased then there is a leak in the form.
    8. Fix the resource leak and  remove the call to GC.Collect(). It isgenerally unnecessary to make an explicit call to GC.Collect() .
    Any suggestions are appreciated.

    Cheers, Eliza
    Thursday, April 15, 2010 7:37 AM
  • In a .net windows application the form might have resource leaks though it is running with managed codes. We can use the below procedure to check if a form is having resource leak.
    1. Open  Windows Task Manager
    2. Click onProcess tab.
    3. Select "View" in the menu and then select "Select Columns" menu item.
    4. Check the USER Objects and GDI Objects (check boxes) to make them appear on the process page list header.
    5. The code in your project that lunches the Win Form, please ensure you have called the Dispose method. Forms implement the IDisposable interface so their dispose method must be called the moment they are no longer needed (Test 1). We can call Dispose explicitly or even better to instantiate it implicitly by the help of using clause (Test 2).
    6. We can use the GC.Collect() after the using statement or after the call to dispose for troubleshooting purpose.
    7. Now time to launch the form. Please note the note the USER Objects and GDI Objects values at the task manager. Close the form after some time and when the form is closed, note the values again at the task manager. We can find the value is decreased if it is increased then there is a leak in the form.
    8. Fix the resource leak and  remove the call to GC.Collect(). It is generally unnecessary to make an explicit call to GC.Collect() .

    Cheers, Eliza
    Thursday, April 15, 2010 8:19 AM