SPLimitedWebPartManager Memory Leak?
June 5th, 2007 . by bryanHave a look at the following code segment:
while(true) { using (SPSite siteCollection = new SPSite(“http://localhost”)) using (SPWeb site = siteCollection.OpenWeb(“/Marketing”)) { SPFolder pagesFolder = site.GetFolder(“Pages”); foreach (SPFile page in pagesFolder.Files) { WL(page.Url); using (SPLimitedWebPartManager webPartManager = page.GetLimitedWebPartManager (PersonalizationScope.Shared)) { } } } }
With this code segment I’m just connecting to a site and iterating over the page collection, grabbing and instance of the page’s SPLimitedWebPartManager as I go. Since SPLimitedWebPartManager implements the IDisposable pattern I am being a good citizen and wrapping its instantiation in a using {} block. The problem with this code segment is that this is what it does to the process memory:
Now, if I change the source code to this:
while(true) { using (SPSite siteCollection = new SPSite(“http://localhost”)) using (SPWeb site = siteCollection.OpenWeb(“/Marketing”)) { SPFolder pagesFolder = site.GetFolder(“Pages”); foreach (SPFile page in pagesFolder.Files) { WL(page.Url); using (SPLimitedWebPartManager webPartManager = page.GetLimitedWebPartManager (PersonalizationScope.Shared)) { webPartManager.Web.Dispose(); } } } }
The memory picture looks much different now:
The only difference being the addition of an explicit Dispose() within the webPartManager using.
A look inside the SPFile.GetLimitedWebPartManager call reveals that it calls an internal GetLimitedWebPartManagerInternal() method on the SPFile SPWeb member variable. A closer look at this method shows the following code:
SPWeb web = this.Site.OpenWeb(); if (this.AllowUnsafeUpdates) { web.AllowUnsafeUpdates = this.AllowUnsafeUpdates; } SPWebPartManager manager = web.GetWebPartManagerInternal(pageUrl, requestedView, forRender, includeHidden, out bytes);
We see here that a new SPWeb object is getting spun up, and in the call to GetWebPartManagerInternal an assignment is going to be made to the SPLimitedWebPartManager m_web member variable. If we look at the Dispose() implementation for SPLimitedWebPartManager, we see the following:
public void Dispose() { if (!this.m_disposed) { if (this.m_manager != null) { this.m_manager.Dispose(); } } else { return; } this.m_webParts = null; this.m_manager = null; this.m_disposed = true; }
Nothing is being done to dispose of the m_web! If I explicitly dispose of m_web, then memory stays low.
Is anyone encountering the same?
Interestingly enough, Tom Sallese and I attended a session at TechEd today covering the WSS object model. One of the things that the speaker covered as a little known “gotcha” was exactly what you are talking about. It appears that we are going to have to be really careful when playing with the SharePoint’s object model. Good looking out B.
Yes, this is exactly what I saw. It didn’t surprise me at all when I got my “outofmemoryexception”; I already had a healthy fear of SharePoint.
Hi,
I fail to understand why you feel that disposing of the web part manager should also dispose its owner web?
for instance, if you have code like this:
spweb w = …..
SPLimitedWebPartManager mgr = w.OpenFile(”…”).GetLimitedWebPartManager(…);
mgr.AddWebPart(…);
mgr.Dispose();
w.Title += ” Done!”;
w.Update();
w.Dispose();
See, I continue to work on the web object after disposing of the limited web part manager… It would be a bug if my w.TItle update will fail… no?
The difference is that the web part manager is not using your w instance, it creates its own instance on the following line:
SPWeb web = this.Site.OpenWeb();
In your example you actually have 2 instances of SPWeb, you have your instance “w”, and then there is an instance encapsulated in the web part manager “web”. The web part manager should dispose of the “web” instance, not your “w” instance.