ASP.NET Case Study: SiteMapResolveEventHandler Memory Leakage

 

I got two memory leak cases last month and the cause is related to SiteMapResolveEventHandler.

 

The general steps on debugging managed memory issue are:

1. Run !dumpheap -stat to output what objects are in the managed heap. Then you need to identify what types of objects increased or occupied the most.

2. Run !gcroot to looks for references to an object.

 

It is normal that only String and Object[] occupied the most:

0:000> !dumpheap –stat

 

        MT Count TotalSize Class Name

 

0x000007fef868a7a0 2,375,378 95,015,120 System.Collections.Specialized.ListDictionary+DictionaryNode

0x000007fef9425a90 2,275,086 261,898,544 System.Object[]

0x000007fef9437ca0 5,816,847 629,217,088 System.String

 

 

 

Because there are so many of these objects, it is not practical to run !gcroot on every String and Object[] object. We have to look for some uncommon types.

In this sample I noticed there were 265 SiteMapResolveEventHandler objects.

 

0:000> !dumpheap –stat

 

        MT Count TotalSize Class Name

 

0x000007fef5878118 265 16,960 System.Web.SiteMapResolveEventHandler

 

SiteMapResolveEventHandler is used for a static event SiteMap.SiteMapResolve. There should not be many SiteMapResolveEventHandler objects.

 

If you have read the artcie .NET Memory Leak Case Study: The Event Handlers That Made The Memory Baloon, you will be familiar with the next steps:

 

Look at one of SiteMapResolveEventHandler objects:

 

0:000> !do 0x0000000167b5db40

Name: System.Web.SiteMapResolveEventHandler

MethodTable: 000007fef5878118

EEClass: 000007fef54ec250

Size: 64(0x40) bytes

GC Generation: 2

 (C:\Windows\assembly\GAC_64\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)

Fields:

              MT Field Offset Type VT Attr Value Name

000007fef9437590 40000ff 8 System.Object 0 instance 167853f60 _target

000007fef9436048 4000100 10 ...ection.MethodBase 0 instance 0 _methodBase

000007fef943a6a8 4000101 18 System.IntPtr 1 instance 8791799744408 _methodPtr

000007fef943a6a8 4000102 20 System.IntPtr 1 instance 0 _methodPtrAux

000007fef9437590 400010c 28 System.Object 0 instance 0 _invocationList

000007fef943a6a8 400010d 30 System.IntPtr 1 instance 0 _invocationCount

 

Dump the target and we will see what object subscribes to it. It is a master page:

0:000> !do 167853f60

Name: ASP.masterpages_default_master

MethodTable: 000007ff0026a818

EEClass: 000007ff00297400

Size: 392(0x188) bytes

GC Generation: 2

 

Then we can directly check the master page code and you will see:

       protected void Page_Load(object sender, EventArgs e)

        {

            SiteMap.SiteMapResolve +=

              new SiteMapResolveEventHandler(this.MySiteMapResolve);

 

        }

 

 

Every time the page which uses this master page is called, a new SiteMapResolveEventHandler object will be created. And the event handler is never removed so the page stays rooted in the event handler object that leaks the memory.

The solution is simple. Remove the event handler in Page_Unload as follows:  

        protected void Page_UnLoad(object sender, EventArgs e)

        {

            SiteMap.Provider.SiteMapResolve -= new SiteMapResolveEventHandler(this. MySiteMapResolve);

        }

 

 

 

Conclusion:

System.Web.SiteMapResolveEventHandler is one of the uncommon types we need to pay attention to when we troubleshoot .NET memory leak issues.

 

Regards,

XinJin from APGC DSI Team