4

After reading some answers/comments on Disposal of SPWebs retrieved from SPContext.Current.Site.AllWebs, I opened up Reflector to dig deeper into the SPSite and SPWeb classes.

I ran into a surprise, which I thought would be worth sharing.

When looking at the implementation for SPSite.Dispose(), which calls SPSite.Close(), I found the following bit of code:

if (this.m_openedWebs != null)
{
    List<SPWeb> list = new List<SPWeb>(this.m_openedWebs);
    foreach (SPWeb web in list)
    {
        web.Close();
    }
}

all SPWeb.Dispose() does, is making a call to SPWeb.Close(), making SPWeb.Close() and SPWeb.Dispose() functionally identical, so essentially when a SPSite is disposed, all SPWebs that appear in the this.m_openedWebs collection are disposed along with it, right? Please correct me if I'm wrong!

At this point I was getting very curious as to how this collection was being populated. I found that when calling SPSite.OpenWeb(string), the new SPWeb is actually added to this collection!

The following chain of methods will be called when calling SPSite.OpenWeb(string)

SPSite.OpenWeb(string) > SPSite.OpenWeb(string, bool) > new SPWeb(SPSite, string, bool) > SPWeb.SPWebConstructor(SPSite, string, bool, SPRequest) > SPSite.AddToOpenedWebs(SPWeb) 

According to Roger Lamb, SPWebs returned by SPSite.OpenWeb() SHOULD be disposed (I am assuming this applies to all overloads for SPSite.OpenWeb()), but based on my findings, I don't see why, as it seems to me that any SPWeb opened using the SPSite.OpenWeb(string) method is automatically disposed (closed) when the SPSite on which OpenWeb() was called is disposed.

I believe it's quite likely I have made some mistake in my thinking somewhere (who am I to question Roger Lamb?), but at this moment I don't know what to believe as I have two contradicting pieces of information.

What do you think?

5 Answers 5

6

Nope, you're definitely right in what you say. Although Roger Lamb's post didn't seem to get updated with this, Stefan talked about it here over a year ago:

Whenever an SPWeb object is created the SPSite object associated with the new SPWeb object automatically adds the new SPWeb object to an internal list. This works for all SPWeb object creations - be it through SPWeb.OpenWeb, SPSite.RootWeb, SPWeb.ParentWeb or any other method that directly or indirectly creates an SPWeb object. When the SPSite object finally gets disposed it will loop through the list and ensure that all SPWeb objects associated with this SPSite object also get disposed.

In my view, because of subtleties like this, I think I like the idea of continuing to explicitly dispose of SPWeb objects created by OpenWeb() etc. Although clearly nothing different will happen underneath, it means the maintenance programmer reading such code doesn't doubt that the disposal will occur i.e. it doesn't require knowledge of every last disposal subtlety, and could perhaps be considered a 'self-documenting code' technique.

Thoughts?

[UPDATE]: Actually immediately after posting, I've made my mind up - it's definitely the right thing to dispose SPWeb objects created this way, purely because the framework guidelines state that where IDisposable is implemented, Dispose() should always be called (for objects you created, of course) when the object is no longer required. QED as they say ;)

1

This is a really good question. I am quite religious when it comes to calling Dispose() on SPWeb and SPSite as well as running SPDisposeCheck. However, I have run plenty of (my own) dodgy beta and pre-release code and never had any real problems.

It is also not completely unlikely that Microsoft has put some extra logic in their own code in one of the many SharePoint Patch releases to auto dispose various resources.

I'll continue to Dispose everything, but I am just worried that I may have turned into one of these monkeys.

0

I can't remember the exact reason off the top of my head but it's to do with object holding onto a reference to the SPWeb object somehow, id have to reasearch it again to work out why but it is generally good practice to surround the OpenWeb() in a using statement.

2
  • I have been wrapping my OpenWeb() calls in using statements, but this new piece of information makes me question whether it's necessary. Commented Jan 11, 2010 at 10:51
  • Yeah your right in what you say, but I remember reading that internally SharePoint holds onto references somehow or I maybe getting confused thats why a explicit call to Dispose() is needed. However Chris make a good point to why to explicity Dispose in regards to following the framework guidelines.
    – Anonymous
    Commented Jan 11, 2010 at 12:15
0

Taken from MSDN:

Several of the Windows SharePoint Services objects, primarily the SPSite class and SPWeb class objects, are created as managed objects. However, these objects use unmanaged code and memory to perform the majority of their work. The managed part of the object is much smaller than the unmanaged part. Because the smaller managed part does not put memory pressure on the garbage collector, the garbage collector does not release the object from memory in a timely manner. The object's use of a large amount of unmanaged memory can cause some of the unusual behaviors described earlier. Calling applications that work with IDisposable objects in Windows SharePoint Services must dispose of the objects when the applications finish using them. You should not rely on the garbage collector to release them from memory automatically.

0

I think this is a new "feature" with SharePoint 2007. There is a registry setting that allows output to the ULS logs details about when an object wasn't disposed properly.
http://msdn.microsoft.com/en-us/library/aa973248.aspx#sharepointobjmodel__findingundisposedobjects

How do they implement this feature unless they are tracking which objects get created and disposed properly? I'm assuming the code you found using reflector was part of this "feature".

I've enabled the heapsettings registry key in my dev environments to try to properly dispose of all objects that I create and I noticed that even the OOTB SharePoint code doesn't properly dispose of objects. One example of this is the blobcache.

Potentially excessive number of SPRequest objects (9) currently unreleased on thread 8. Ensure that this object or its parent (such as an SPWeb or SPSite) is being properly disposed. This object is holding on to a separate native heap. Allocation Id for this object: {9BF59145-A85C-4FA8-9EF6-F5604DB2445C} Stack trace of current allocation: at Microsoft.SharePoint.SPRequestManager.Add(SPRequest request, Boolean shareable) at Microsoft.SharePoint.SPGlobal.CreateSPRequestAndSetIdentity(Boolean bNotGlobalAdminCode, String strUrl, Boolean bNotAddToContext, Byte[] UserToken, String userName, Boolean bIgnoreTokenTimeout, Boolean bAsAnonymous) at Microsoft.SharePoint.SPWeb.InitializeSPRequest() at Microsoft.SharePoint.SPWeb.EnsureSPRequest() at Microsoft.SharePoint.SPWeb.get_Request() at Microsoft.SharePoint.SPWeb.InitWebPublic() at Microsoft.SharePoint.SPWeb.get_ServerRelativeUrl() at Microsoft.SharePoint.SPChangeCollection.InitChangeCollection() at Microsoft.SharePoint.SPChangeCollection.ConstructChangeCollection(SPChangeQuery query) at Microsoft.SharePoint.SPChangeCollection..ctor(SPSite site, SPChangeQuery query) at Microsoft.SharePoint.SPSite.GetChanges(SPChangeToken changeToken, SPChangeToken changeTokenEnd) at Microsoft.SharePoint.Publishing.BlobCache.<>c__DisplayClasse.b__a() at Microsoft.Office.Server.Diagnostics.FirstChanceHandler.ExceptionFilter(Boolean fRethrowException, TryBlock tryBlock, FilterBlock filter, CatchBlock catchBlock, FinallyBlock finallyBlock) at Microsoft.Office.Server.Diagnostics.ULS.SendWatsonOnExceptionTag(ULSTagID tagID, ULSCat categoryID, String output, Boolean fRethrowException, TryBlock tryBlock, CatchBlock catchBlock, FinallyBlock finallyBlock) at Microsoft.SharePoint.Publishing.BlobCache.CheckSiteChanges(Boolean checkForPolicyChange) at Microsoft.SharePoint.Publishing.BlobCache.<>c__DisplayClass6.b__5() at Microsoft.SharePoint.SPSecurity.CodeToRunElevatedWrapper(Object state) at Microsoft.SharePoint.SPSecurity.<>c__DisplayClass4.b__2() at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode) at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param) at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode) at Microsoft.SharePoint.Publishing.BlobCache.LoopForChanges() at Microsoft.SharePoint.Publishing.BlobCache.GetCacheTokenThreadStart() at System.Threading.ExecutionContext.runTryCode(Object userData) at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.