When to Dispose SharePoint objects


Table of Contents
SharePoint Dispose Checker Tool
Microsoft have been using a tool internally for a while now and finally released it to the general community. It will highlight various issues with your code where you have not disposed SharePoint Object Model code correctly. Please bare in mind that it does not check for all of the issues identified below, but is a good start! The tool can be downloaded from Code.MDSN and is based on the guidance listed on Roger Lamb's SharePoint 2007 and WSS 3.0 Dispose Patterns by Example.

How to integrate SP Dispose Check into Visual Studio Solutions

SPSite and SPWeb implement the IDisposable interface, which indicates they hold resources that need to be released when they are no longer needed. Failing to correctly Dispose() can cause unnecessary memory pressure on a SharePoint server.

How to source problems in existing code bases
You can view the ULS logs in the 12 HIVE and search for entries that mention 'SPRequest object was not disposed before the end of this thread' errors. This occured a lot in SharePoint 2007 RTM due to Microsoft's own ommissions with disposing of SPWeb. These errors are not all that helpful as they don't identify which codebase is causing the issue. This can be resolved by create/modifying the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings and then create a new DWORD named SPRequestStackTrace with the value 1 under this key. Once it is set, you'll be able to see the stack trace in your custom offending code. Then you can go identify the cause of the problem and follow the steps below to fix it.

General Guidelines

1. Using a disposed object can cause more problems than failing to dispose.

There are some situations where it is not clear if an object is safe to Dispose or not. When in doubt, err on the side of caution and let SharePoint clean up after itself. All SPWeb objects will be disposed as part of their parent SPSite's disposal process. Similarly, all SPSite objects are disposed when the thread ends. A potential memory leak is probably preferred to receiving an exception: "Trying to use an SPWeb object that has been closed or disposed and is no longer valid."

2. An object should be disposed as soon as it is no longer needed.

An object can be safely disposed after the last access to a child object of that object. A child object is any object (or child of an object) that stores a reference to the object from which it is created. For example, an SPListItem is a child of an SPWeb, which is a child of an SPSite.

3. Objects should be disposed in the same method where they are allocated.

The easiest mechanism for ensuring proper disposal is the using statement (C#, VB), which compiles into a try...finally block that will always call Dispose() on the given object, even if there is a return within the code block. However, this can only be used if the object is not needed outside the using block, including references from child objects.

A common anti-pattern looks something like this:

SPList GetList(string webUrl, string listName)
{
  SPSite site = new SPSite(webUrl);
  SPWeb web = site.OpenWeb();
  return web.Lists[listName];
}
void DoSomething(string webUrl, string listName)
{
  SPList list = GetList(webUrl, listName);
  // Do something with list

  list.ParentWeb.Site.Dispose();
}

With the object instantiation and disposal separated, it is much more likely that something will get disposed too early or not at all. A better approach might look like this:

void DoSomethingHelper(SPWeb web, string listName)
{
  SPList list = web.Lists[listName];
  // Do something with list
}

void DoSomething(string webUrl, string listName)
{
  using(SPSite site = new SPSite(webUrl)
  {
    using(SPWeb web = site.OpenWeb())
    {
      DoSomethingHelper(web, listName);
    }
  }
}

4. Be extremely careful with iterating through collections.

There are some circumstances where it is necessary to iterate through collections of SPWeb or SPSite objects.

Performance
Iterating over large collections of these objects is expensive and should only be done when absolutely necessary. Consider using SPNavigationProvider or PortalSiteMapProvider (MOSS only) instead.
//loop through each sub site
foreach (SPWeb subweb in rootweb.Webs)
{
  //do stuff
}

In the example above this would not be disposing of each subweb correctly. The below sample shows how this can be handled correctly.

//loop through each sub site
for (int i = 0; i <= rootweb.Webs.Count; i++)
{
  using (SPWeb subweb = rootweb.Webs[i])
  {
    //do stuff
  }
}

To safely use these collections with LINQ, consider using an iterator: Source

public static IEnumerable<SPWeb> AsSafeEnumerable(this SPWebCollection webs)
{
  foreach (SPWeb web in webs)
  {
    try
    {
      yield return web;
    }
    finally
    {
      web.Dispose();
    }
  }
}

Usage:

var lists = from w in site.AllWebs.AsSafeEnumerable()
            from SPList l in w.Lists
            where !l.Hidden && !w.IsRootWeb
            select new { WebTitle = w.Title, ListTitle = l.Title };

5. Only dispose objects owned by the developer's code.

Keeping Guideline 1 in mind, here are the common sources of SPSite and SPWeb objects that may or may not be the developer's responsibility to Dispose.

Objects owned by the developer:

SPSite Constructor
using(SPSite site = new SPSite(url))
{
  // Process site
}
SPSite.SelfServiceCreateSite()
using(SPSite site = new SPSite(url))
{
  using(SPSite siteSelfServ = site.SelfServiceCreateSite("Absolute_URL", "Title", ...))
  {
    // Process siteSelfServ
  }
}
SPSite.OpenWeb()
using(SPWeb web = site.OpenWeb())
{
  // Process web
}
SPWebCollection and SPSiteCollection

These collections are returned by various methods and properties:

Objects retrieved by adding to, indexing into or enumerating over these collections always need to be disposed.

using(SPSite site = webApp.Sites[0])
{
  using(SPWeb newWeb = site.AllWebs.Add("NewWeb"))
  {
    // Process newWeb
  }

  foreach(SPWeb web in site.RootWeb.GetSubwebsForCurrentUser())
  {
    try
    {
      // Process web
    }
    finally
    {
      web.Dispose();
    }
  }
}
Microsoft.SharePoint.SPWeb.GetLimitedWebPartManager()

The SPLimitedWebPartManager object returned by this method holds a reference to an internal SPWeb that it will not dispose. The developer needs to call Dispose() explicitly.

SPFile page = web.GetFile("default.aspx");
using (SPLimitedWebPartManager webPartManager =
  page.GetLimitedWebPartManager(PersonalizationScope.Shared))
{
  try
  {
    // Process webPartManager
  }
  finally
  {
    webPartManager.Web.Dispose();
  }
}
Microsoft.SharePoint.Publishing.PublishingWebCollection (MOSS 2007)

Objects retrieved by indexing into or enumerating over this collection will hold a reference to an internal SPWeb that will not be disposed by Close(). Instead, the developer needs to call Dispose() on the PublishingWeb's Web.

using(SPWeb web = site.OpenWeb())
{
  PublishingWeb pubWeb = PublishingWeb.GetPublishingWeb(web);
  PublishingWebCollection pubWebs = pubWeb.GetPublishingWebs());
  foreach(PublishingWeb innerPubWeb in pubWebs)
  {
    try
    {
      // Process innerPubWeb
    }
    finally
    {
      innerPubWeb.Web.Dispose();
    }
  }
}
Microsoft.SharePoint.Publishing.PublishingWebCollection.Add() (MOSS 2007)

The developer needs to call Close() on the returned PublishingWeb object.

using(SPWeb web = site.OpenWeb())
{
  PublishingWeb newPubWeb = null;
  try
  {
    PublishingWeb pubWeb = PublishingWeb.GetPublishingWeb(web);
    PublishingWebCollection pubWebs = pubWeb.GetPublishingWebs();
    newPubWeb = pubWebs.Add("NewPubWeb");
  }
  finally
  {
    if(null != newPubWeb)
      newPubWeb.Close();
  }
}
Microsoft.SharePoint.Publishing.PublishingWeb.GetVariation() (MOSS 2007)

The developer needs to call Close() on the returned PublishingWeb object.

using(SPWeb web = site.OpenWeb())
{
  PublishingWeb varPubWeb = null;
  try
  {
    PublishingWeb pubWeb = PublishingWeb.GetPublishingWeb(web);
    VariationLabel label = Variations.Current[0];
    varPubWeb = pubWeb.GetVariation(label);
    // Process varPubWeb
  }
  finally
  {
    if(null != varPubWeb)
      varPubWeb.Close();
  }
}
Microsoft.Office.Server.UserProfiles.UserProfile.PersonalSite (MOSS 2007)

If the developer's code accesses this property, it creates an SPSite that needs to be disposed. Source

// Use ServerContext.Current in web context
ServerContext context = ServerContext.GetContext(site);

UserProfile myProfile = ProfileLoader.GetProfileLoader(context).GetUserProfile();
using (SPSite personalSite = profile.PersonalSite)
{
  // Process personalSite
}
This guidance does not apply to the PersonalSite or PersonalWeb properties of My Site pages that implement IPersonalPage. Objects returned by these properties should be considered owned by SharePoint.
Miscellaneous

Objects owned by SharePoint:

SPSite.RootWeb

This property returns a shared SPWeb instance and is used internally in several methods and properties. It should never be disposed explicitly. Source

SPSite site = SPContext.Current.Site;
SPWeb rootWeb = site.RootWeb;
// Process rootWeb

// NO rootWeb.Dispose()
SPContext
  • SPContext.Web (and SPContext.Current.Web)
  • SPContext.Site (and SPContext.Current.Site)
Feature Receiver

SPFeatureReceiverProperties.Feature.Parent for a Site- or Web-scoped feature receiver should not be disposed.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
  SPWeb web = properties.Feature.Parent as SPWeb;
  // Process web

  // NO web.Dispose()
}
Event Receiver
  • SPWebEventProperties.Web
  • SPListEventProperties.Web
  • SPListEventProperties.List.Web
  • SPItemEventProperties.ListItem.Web

SPItemEventProperties implements IDisposable to clean up the SPSite it creates; the others do not. It is not yet clear if the framework actually handles these correctly, so it may be safer to avoid these properties in favor of creating a new developer-owned SPSite object.

public override void ItemAdded(SPItemEventProperties properties)
{
  using (SPSite site = new SPSite(properties.WebUrl))
  {
    using(SPWeb web = site.OpenWeb())
    {
      SPList list = web.Lists[properties.ListId];
      SPListItem item = list.GetItemById(properties.ListItemId);

      // Process item instead of properties.ListItem
    }
  }
  base.ItemAdded(properties);
}
Microsoft.SharePoint.Portal.WebControls.IPersonalPage (MOSS 2007)
  • IPeronsalPage.PersonalSite
  • IPersonalPage.PersonalWeb

My Site pages, which implement IPersonalPage, implement these properties as shared instances that should not be disposed by controls that use them.

IPersonalPage currentMySitePage = this.Page as IPersonalPage;
if (currentMySitePage != null && !currentMySitePage.IsProfileError)
{
  SPSite personalSite = currentMySitePage.PersonalSite;
  // Process personalSite

  // NO personalSite.Dispose()
}
Miscellaneous
  • UnsecuredLayoutsPage.Web (and LayoutsPageBase.Web)
  • SPControl.GetContextWeb()
  • SPControl.GetContextSite()
  • SPWebProvisioningProperties.Web

Objects that might be owned by the developer:

SPWeb.ParentWeb

This property will allocate an SPWeb object the first time it is called. The caveat is that once it is disposed, any reference to the property will return the disposed object. If an SPWeb is not owned by the developer, its ParentWeb should be considered not owned as well. For example, there could be a problem if two components both depend on SPContext.Current.Web.ParentWeb and one calls Dispose() before the other is done with it. Official Microsoft guidance is to never explicitly Dispose() ParentWeb.

Additional Resources

Labels

dispose dispose Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.



Creative Commons License
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License. Hosted generously by CustomWare