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