Why isn't garbage collection getting run on my dynamically loaded forms?

I am working on a project that loads forms from assemblies at runtime.
All was working fine until some clients got an OutOfMemoryException.

After some research, I have found that memory remains allocated for the form after it is disposed.
My thought is that the GC is not getting run or is being run out of scope of the dynamically loaded forms.

Below is the code for the Form Loader.
Any ideas?
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Reflection;
using ABS.BusinessObjects.EntityClasses;
 
namespace ABS.UI.Main
{
    class FormLoader : ABS.UI.Main.IFormLoader
    {
        private List<Form> _LoadedForms = new List<Form>();
 
        private Form _ParentForm;
 
        public FormLoader(Form parentForm)
        {
            this._ParentForm = parentForm;
        }
 
        public void LoadForm(MenuItemEntity menuItemEntity, object[] arguments)
        {
            try
            {
                if ((menuItemEntity == null) ||
                    (menuItemEntity.AssemblyFilePath == "") ||
                    (menuItemEntity.ClassName == ""))
                {
                    return;
                }
 
                if (!File.Exists(menuItemEntity.AssemblyFilePath))
                {
                    MessageBox.Show(menuItemEntity.AssemblyFilePath + " does not exist.");
                }
 
                Assembly assembly = Assembly.LoadFile(Application.StartupPath + "\\" + menuItemEntity.AssemblyFilePath);
 
                if (assembly == null)
                {
                    throw new Exception("Assembly: " + menuItemEntity.AssemblyFilePath + " could not be loaded.");
                }
 
                Type type = assembly.GetType(menuItemEntity.ClassName, true, false);
                
                object form = assembly.CreateInstance(
                    type.FullName,
                    false,
                    BindingFlags.CreateInstance,
                    null,
                    arguments,
                    null,
                    null
                );
 
                ((Form)form).MdiParent = this._ParentForm;
                ((Form)form).StartPosition = FormStartPosition.Manual;
                ((Form)form).Location = new System.Drawing.Point(0, 0);
                //((Form)form).FormClosed += new FormClosedEventHandler(FormLoader_FormClosed);
                ((Form)form).Disposed += new EventHandler(FormLoader_Disposed);
                ((Form)form).Show();
                this._LoadedForms.Add(((Form)form));
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
 
        void FormLoader_Disposed(object sender, EventArgs e)
        {
            int index = this._LoadedForms.IndexOf(((Form)sender));
 
            if (index >= 0)
            {
                Form form = this._LoadedForms[index];
                this._LoadedForms.Remove(form);
                form = null;
            }
        }
 
        void FormLoader_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (sender is Form)
            {
                Form form = (Form)sender;
 
                if (form.IsDisposed)
                {
                    form = null;
                    sender = null;
                    GC.Collect();
                    return;
                }
 
                form.Dispose();
 
                form = null;
                sender = null;
 
                GC.Collect();
 
                return;
            }
 
            if (sender is Control)
            {
                Control control = (Control)sender;
 
                if (control.IsDisposed)
                {
                    control = null;
                    sender = null;
                    GC.Collect();
                    return;
                }
 
                control.Dispose();
 
                control = null;
                sender = null;
 
                GC.Collect();
 
                return;
            }
 
            MessageBox.Show("Form has closed but could not be disposed.");
        }
    }
}

Open in new window

LVL 3
jeshbrAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

gregoryyoungCommented:
I would be willing to bet that you are holding references to the forms (gc is not broken).  


Have you ever used SOS (its a CLR level debugging tool)? It can show you where the references are being held. If you can come up with a more complete example and don't know how to use SOS I can run it for you on an example.


Taking a shot in the dark ...  I might guess an event some place (if not untied the form will stay alice while the object producing the event is still alive).


Aside from that ... are you sure that the dynamically loaded form is raising its Disposed event when disposed is called (is it being overriden in some forms but not raising event etc?). You only remove it from your list of forms when the dispose event is raised. Personally I would probably change this as its bad to have a plugin that can get itself into trouble like this.

Cheers,

Greg
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
jeshbrAuthor Commented:
The 'Dispose' event is definitely being called.
I would think that the resources allocated for the form would be released after the dispose was complete.

The loaded forms list is only there due to my troubleshooting of this issue.
It will be removed after I have found a solution.

I will look at SOS.
Maybe it will shed some light on this issue.

I will also try to come up with a better example.

Any other ideas will be greatly appreciated also.

Thanks.
0
jeshbrAuthor Commented:
This is what I am finding.
The form is definitely in memory after it is disposed.

!gcroot -nostacks 0c494300
DOMAIN(0016BF18):HANDLE(WeakLn):901018:Root:012fc464(System.Windows.Forms.NativeMethods+WndProc)->
012f76dc(System.Windows.Forms.Control+ControlNativeWindow)->
012f7648(System.Windows.Forms.MdiClient)->
012f76b4(System.Collections.ArrayList)->
015557ac(System.Object[])->
0c5d47d4(ABS.UI.BuyerMaintenanceForm)->
0c5d77c8(System.Windows.Forms.ToolStripComboBox)->
0c5d7858(System.Windows.Forms.ToolStripComboBox+ToolStripComboBoxControl)->
0c640fd4(System.Windows.Forms.BindingSource)->
0c2cd928(System.ComponentModel.PropertyDescriptorCollection)->
0c2cd908(System.Object[])->
0c2cd840(System.ComponentModel.ReflectPropertyDescriptor)->
0c2ce328(System.Collections.Hashtable)->
0c2ce360(System.Collections.Hashtable+bucket[])->
0c645464(System.EventHandler)->
0c64544c(System.Object[])->
0c52e708(System.EventHandler)->
0c52dffc(System.Windows.Forms.BindingSource)->
0c52e0e8(System.ComponentModel.EventHandlerList)->
0c532468(System.ComponentModel.EventHandlerList+ListEntry)->
0c532424(System.ComponentModel.EventHandlerList+ListEntry)->
0c532404(System.EventHandler)->
0c494300(ABS.UI.BuyerMaintenanceForm)
DOMAIN(0016BF18):HANDLE(WeakLn):901020:Root:012fc224(System.Windows.Forms.NativeMethods+WndProc)->
012ea560(System.Windows.Forms.Control+ControlNativeWindow)->
012ea324(System.Windows.Forms.MenuStrip)->
012e7a1c(ABS.UI.Main.MainForm)->
012e7f50(System.Windows.Forms.PropertyStore)->
0c507b44(System.Windows.Forms.PropertyStore+ObjectEntry[])
DOMAIN(0016BF18):HANDLE(WeakLn):901500:Root:0c506b80(System.Windows.Forms.NativeMethods+WndProc)->
0c4d8acc(System.Windows.Forms.Control+ControlNativeWindow)->
0c4d8a14(MSF.Windows.Forms.TextBox)->
0c4da440(System.ComponentModel.EventHandlerList)->
0c52cbd8(System.ComponentModel.EventHandlerList+ListEntry)->
0c52cb7c(System.ComponentModel.EventHandlerList+ListEntry)->
0c4da4b8(System.ComponentModel.EventHandlerList+ListEntry)->
0c4da498(System.Windows.Forms.KeyEventHandler)->
0c494300(ABS.UI.BuyerMaintenanceForm)
DOMAIN(0016BF18):HANDLE(WeakLn):9017c4:Root:0c505be4(System.Windows.Forms.NativeMethods+WndProc)->
0c4946c8(System.Windows.Forms.Control+ControlNativeWindow)->
0c494300(ABS.UI.BuyerMaintenanceForm)
DOMAIN(0016BF18):HANDLE(WeakLn):9017f8:Root:0c5069f8(System.Windows.Forms.NativeMethods+WndProc)->
0c4d8c88(System.Windows.Forms.Control+ControlNativeWindow)->
0c4d8bb8(MSF.Windows.Forms.Button)->
0c4da4cc(System.ComponentModel.EventHandlerList)->
0c4da54c(System.ComponentModel.EventHandlerList+ListEntry)->
0c4da52c(System.EventHandler)->
0c494300(ABS.UI.BuyerMaintenanceForm)
DOMAIN(0016BF18):HANDLE(WeakSh):902344:Root:0c494b04(System.Windows.Forms.Control+ControlNativeWindow)->
0c494978(System.Windows.Forms.ToolStrip)->
0c495544(System.Windows.Forms.ToolStripItemCollection)->
0c49555c(System.Collections.ArrayList)->
0c4cb1b4(System.Object[])->
0c49832c(System.Windows.Forms.ToolStripButton)->
0c49b024(System.ComponentModel.EventHandlerList)->
0c4c5bc0(System.ComponentModel.EventHandlerList+ListEntry)->
0c4c5ba0(System.EventHandler)->
0c494300(ABS.UI.BuyerMaintenanceForm)
DOMAIN(0016BF18):HANDLE(WeakSh):902348:Root:0c4946c8(System.Windows.Forms.Control+ControlNativeWindow)->
013cd820(System.Windows.Forms.Control+ControlNativeWindow)

How can I eliminate all of the references?
0
CompTIA Cloud+

The CompTIA Cloud+ Basic training course will teach you about cloud concepts and models, data storage, networking, and network infrastructure.

mastooCommented:
You might be seeing normal gc behavior (a non-problem).  Take a look at this, ignoring the miles of code in the original post:

http://www.experts-exchange.com/Programming/Languages/C_Sharp/Q_22451317.html
0
jeshbrAuthor Commented:
The problem that I have is that the forms resources are not being cleared from memory.
I realize that the GC will run when it wants/needs to. But the resources are not being cleared even when I try to force the GC to collect.

Clients with 512 mb memory will and are getting OutOfMemoryExceptions after using the system for short periods of time.

They are closing out of the forms, the forms 'Disposed' event is getting thrown (I have watched it) and the resources are not getting released.
0
gregoryyoungCommented:
from looking at your gcroots it looks like you are passing around the reference to the dynamically loaded form and saving it a good bit.

The ones I would be most worried about are ones like

012f76b4(System.Collections.ArrayList)->
015557ac(System.Object[])->
0c5d47d4(ABS.UI.BuyerMaintenanceForm)->
012e7a1c(ABS.UI.Main.MainForm)->
0c49555c(System.Collections.ArrayList)->
0c4cb1b4(System.Object[])->
0c494300(ABS.UI.BuyerMaintenanceForm)

Why do you have other forms holding a reference to this form? Also have you checked to insure that this object that you are looking at gcroots for is actually disposed?

Cheers,

Greg
0
jeshbrAuthor Commented:
The form is subclassed from a base class of MSF.UI.MaintenanceFormBase.
MSF.UI.MaintenanceFormBase has events that are used in ABS.UI.BuyerMaintenanceForm.

Ex. this.AddingNewChanged += new AddingNewChangedEventHandler(this.BuyerMaintenanceForm_AddingNewChanged);

Do I need to unsubscribe all of the MSF.UI.MaintenanceFormBase events?

Ex.  this.AddingNewChanged -= new AddingNewChangedEventHandler(this.BuyerMaintenanceForm_AddingNewChanged);

I downloaded a trial version of ANTS Profiler.
Is there anything in that program that I can post here that would be helpful?
0
gregoryyoungCommented:
yes you do need to unsubscribe events (this is what I mentioned in my previous post about events). but generally only when they are amoung multiple objects i.e.

in your example of ...

this.AddingNewChanged += new AddingNewChangedEventHandler(this.BuyerMaintenanceForm_AddingNewChanged);
These are both on "this" which isn't a problem ...

what is a problem is when you start doing things like ...

SomeOtherForm.SomeEvent += new WhateverHandler(this.Whatever);

because in doing this a reference to this is created in SomeOtherForm (the delegate). Since the reference is there "this" will not be eligible for garbage collection until SomeOtherForm is also eligible for garbage collection because it holds references to "this". The way around this is to untie such events.

Does that make sense?
0
jeshbrAuthor Commented:
That makes sense.

I may be missing something on the SOS information.
Is there a way to see what object/event my form is tied to?
0
gregoryyoungCommented:
http://www.julmar.com/blog/mark/default,month,2006-09.aspx includes instructions.

be warned ... you are enterring the dark side of .NET debugging :)

Cheers,

Greg
0
jeshbrAuthor Commented:
I am learning more than I ever wanted to learn about the inner workings of .NET.
I had not seen assembly code since college. And I was happy with that. :)

I have learned a lot about some new (to me) tools.
I am now using ADPlus, WinDbg and SOS.

Here is what I got so far.

I used ADPlus to make a full dump of my application, loaded the dump into WinDbg and loaded SOS.
I had some problems with SOS in VS2008 so I abandoned it.




We will start at the GCRoot for the BuyerMaintenanceForm.

0:000> !gcroot 013cd428
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
eax:Root:012fbfa4(System.Windows.Forms.Application+ComponentManager)->
012e82ac(System.Windows.Forms.Application+ThreadContext)->
012e789c(ABS.UI.Main.MainForm)->
012e7dd4(System.Windows.Forms.PropertyStore)->
01455c34(System.Windows.Forms.PropertyStore+ObjectEntry[])
Scan Thread 0 OSTHread 13ac
Scan Thread 2 OSTHread 1ac
Scan Thread 9 OSTHread 13c4
Scan Thread 11 OSTHread 704
Scan Thread 12 OSTHread 1104
DOMAIN(0016BF18):HANDLE(Pinned):9013e8:Root:022d4dc8(System.Object[])->
013328ec(System.ComponentModel.WeakHashtable)->
0144e0f8(System.Collections.Hashtable+bucket[])->
01332bac(System.ComponentModel.TypeDescriptor+TypeDescriptionNode)->
01332b98(System.ComponentModel.ReflectTypeDescriptionProvider)->
01332ca8(System.Collections.Hashtable)->
0144d94c(System.Collections.Hashtable+bucket[])->
01332c80(System.ComponentModel.ReflectTypeDescriptionProvider+ReflectedTypeData)->
014549e8(System.ComponentModel.PropertyDescriptorCollection)->
014549d4(System.Object[])->
0145490c(System.ComponentModel.ReflectPropertyDescriptor)->
01454bf0(System.Collections.Hashtable)->
01454c28(System.Collections.Hashtable+bucket[])->
01453c00(System.EventHandler)->
014539ec(System.Windows.Forms.BindingSource)->
01453ac0(System.ComponentModel.EventHandlerList)->
01454cec(System.ComponentModel.EventHandlerList+ListEntry)->
01454cd8(System.ComponentModel.EventHandlerList+ListEntry)->
01454cb8(System.EventHandler)->
013cd428(ABS.UI.BuyerMaintenanceForm)
DOMAIN(0016BF18):HANDLE(WeakLn):901928:Root:0144f370(System.Windows.Forms.NativeMethods+WndProc)->
013cdd90(System.Windows.Forms.Control+ControlNativeWindow)->
013cd428(ABS.UI.BuyerMaintenanceForm)
DOMAIN(0016BF18):HANDLE(WeakLn):902178:Root:0144f490(System.Windows.Forms.NativeMethods+WndProc)->
013cee9c(System.Windows.Forms.Control+ControlNativeWindow)->
013ced10(System.Windows.Forms.ToolStrip)->
013cf578(System.Windows.Forms.ToolStripItemCollection)->
013cf590(System.Collections.ArrayList)->
013da564(System.Object[])->
013d1384(System.Windows.Forms.ToolStripButton)->
013d3c14(System.ComponentModel.EventHandlerList)->
013da274(System.ComponentModel.EventHandlerList+ListEntry)->
013da254(System.EventHandler)->
013cd428(ABS.UI.BuyerMaintenanceForm)
DOMAIN(0016BF18):HANDLE(WeakLn):902180:Root:0144f450(System.Windows.Forms.NativeMethods+WndProc)->
013db860(System.Windows.Forms.Control+ControlNativeWindow)->
013db7a8(MSF.Windows.Forms.TextBox)->
0143a89c(System.ComponentModel.EventHandlerList)->
014539d8(System.ComponentModel.EventHandlerList+ListEntry)->
014539c4(System.ComponentModel.EventHandlerList+ListEntry)->
0143a8cc(System.ComponentModel.EventHandlerList+ListEntry)->
0143a8ac(System.Windows.Forms.KeyEventHandler)->
013cd428(ABS.UI.BuyerMaintenanceForm)
DOMAIN(0016BF18):HANDLE(WeakLn):902184:Root:0144f430(System.Windows.Forms.NativeMethods+WndProc)->
013db9dc(System.Windows.Forms.Control+ControlNativeWindow)->
013db90c(MSF.Windows.Forms.Button)->
0143a8e0(System.ComponentModel.EventHandlerList)->
0143a910(System.ComponentModel.EventHandlerList+ListEntry)->
0143a8f0(System.EventHandler)->
013cd428(ABS.UI.BuyerMaintenanceForm)
DOMAIN(0016BF18):HANDLE(WeakSh):902a64:Root:013cdd90(System.Windows.Forms.Control+ControlNativeWindow)




Now I'll take a look at one of the event handlers.

0:000> !do 01454cb8
Name: System.EventHandler
MethodTable: 79329b58
EEClass: 790c39d0
Size: 32(0x20) bytes
 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
79330508  40000ff        4        System.Object  0 instance 013cd428 _target
7932fd60  4000100        8 ...ection.MethodBase  0 instance 00000000 _methodBase
793331b4  4000101        c        System.IntPtr  1 instance  33f6a44 _methodPtr
793331b4  4000102       10        System.IntPtr  1 instance        0 _methodPtrAux
79330508  400010c       14        System.Object  0 instance 00000000 _invocationList
793331b4  400010d       18        System.IntPtr  1 instance        0 _invocationCount




Now I'll take a look at the _methodPtr.

0:000> !u 33f6a44
Unmanaged code
033f6a44 b8f443ad00      mov     eax,0AD43F4h
033f6a49 90              nop
033f6a4a e841c4a776      call    mscorwks!PrecodeRemotingThunk (79e72e90)
033f6a4f e9b8b4f7fc      jmp     00371f0c
033f6a54 b80044ad00      mov     eax,0AD4400h
033f6a59 90              nop
033f6a5a e831c4a776      call    mscorwks!PrecodeRemotingThunk (79e72e90)
033f6a5f e944675201      jmp     0491d1a8
033f6a64 b80c44ad00      mov     eax,0AD440Ch
033f6a69 90              nop



I am not sure where to go from here.
mscorwks is a core part of the .NET Runtime.
I don't see what event is being subscribed to.
My Google search of 'PrecodeRemotingThunk' has been fruitless.

I will keep digging deeper but I think I'm close if not way above my level of understanding.
Any ideas?


0
gregoryyoungCommented:
Are you using remoting?

I can explain what the remoting thunk is but is may not be a whole lot of help to you... basically it is a bit of generated code to handle the interop with remoting. Consider it this way ... it is code that is generated to stand in place, if it makes it more clear here is the code from the open source version of the clr (rotor) that handles this http://www.koders.com/cpp/fid5176733D109ED3E543B387811D78042B49DACC79.aspx?s=CmpXchgOps.

I wish I could be of more help to you than this but as I am sure you are finding out this process requires a rather large amount of knowledge about what else is going on in your system (like looking at / understanding your source code etc)

Cheers,

Greg
0
jeshbrAuthor Commented:
I am not using remoting.
At least I was not aware that I was.

I am using the LLBLGen framework for some of my BOs.
LLBLGen does support .NET remoting but I am not using it to my knowledge.
Perhaps the framework has some remoting features that are being wired up?
But I don't think so.
0
jeshbrAuthor Commented:
I do have some classes that are Serializable because they need to be saved to XML and then loaded from the XML later.

I don't know if that would make cause these references to be created.
But I am not doing any actual remoting.
0
gregoryyoungCommented:
You may be using it without knowing it (like interacting between multiple app domains)
0
jeshbrAuthor Commented:
Please excuse my ignorance.
How could I be accessing multiple app domains without knowing?

Another thing that might be useful.
I originally thought that the problem may be due to the way that I was dynamically loading the forms.
This is not the problem.

I added a reference to a few forms and created them the regular way.
The problem remained.
0
gregoryyoungCommented:
well libraries that you are using could be using multiple app domains. Depending on how you are doing your dynamic loading it could be multiple app domains ...

You can see your application domains with SOS.

Cheers,

Greg
0
jeshbrAuthor Commented:
I did this:

0:000> !DumpDomain
--------------------------------------
System Domain: 7a3bd058
LowFrequencyHeap: 7a3bd07c
HighFrequencyHeap: 7a3bd0c8
StubHeap: 7a3bd114
Stage: OPEN
Name: None
--------------------------------------
Shared Domain: 7a3bc9a8
LowFrequencyHeap: 7a3bc9cc
HighFrequencyHeap: 7a3bca18
StubHeap: 7a3bca64
Stage: OPEN
Name: None
Assembly: 001b3b28
--------------------------------------
Domain 1: 0016bf18
LowFrequencyHeap: 0016bf3c
HighFrequencyHeap: 0016bf88
StubHeap: 0016bfd4
Stage: OPEN
SecurityDescriptor: 0016d240
Name: ABS.UI.Main.exe
Assembly: 001b3b28 [C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 001b3ba8
SecurityDescriptor: 001b3e48
  Module Name
790c1000 C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll
00942354 C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\sortkey.nlp
00942010 C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\sorttbls.nlp

......
...
.


I only see one domain.

You can download the dump and look at it if you want.
I realize that you don't know what the application is doing but maybe you will see something that I am missing.

http://www.comsoftdev.com/downloads/Q_23872804_Application_Dump.rar

0
jeshbrAuthor Commented:
I found the event holding the reference.
The form had a BindingSource that some events were subscribed to.

BuyerMaintenanceForm is a subclass of MaintenanceFormBase.
MaintenanceFormBase has a BindingSource in it called 'SortBindingSource'.

I was subscribing to a few events on the MaintenanceFormBase's 'SortBindingSource' object.
There must be a problem with binding to parent object events.

Anyways after a lot of digging and hairpulling.... all is well.

Thanks for your help.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.