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