SharePoint 2007 and WSS 3.0 Dispose Patterns by Example


Blog Post Revised 12/13/2010

Overview

SharePoint Server development has some gotchas with serious implications that every application developer needs to be intimately familiar with before deploying into production farms.  In particular, Microsoft.SharePoint.SPSite , Microsoft.SharePoint.SPWeb , and the often overlooked Microsoft.SharePoint.Publishing objects need to be carefully examined and disposed of properly in order to avoid potential memory leaks. My original objective with this blog post was to consolidate several different reliable sources compiled by Microsoft’s top Subject Matter Experts (SME’s) into a Uber-Dispose quick reference which could be used to increase developer awareness of the current Microsoft Guidance.  I’ve been working closely with the SharePoint Product Group as well as the top Microsoft Support and Services SME’s to help carve out the information you see here.  I plan to keep this blog post updated with the latest official guidance from Microsoft and while there are more verbose resources available elsewhere the primary objective is to provide lots of samples in a quick reference format.  Pre-requisites: The intended audience for this guidance is a seasoned .NET developer who currently is or will be developing custom SharePoint solutions with Visual Studio.

Additional Resources:


Quick Reference – SharePoint Example Dispose() Patterns

Why Dispose?

SPSite and SPWeb classes both implement the IDisposable interface.  Microsoft .NET requires objects that implement the IDisposable interface to properly cleanup the unmanaged resources by explicitly calling the Dispose() method when you are finished using them.  Internally, SPSite and SPWeb both hold references to an "internal class Microsoft.SharePoint.Library.SPRequest" which holds on to unmanaged COM resources.  The consequence of not explicitly disposing unmanaged resources in a timely fashion can lead to not having enough memory for further allocations and quickly consumes memory. Under the hood, when reviewing dump files we see the that the managed objects used by SPSite and SPWeb are relatively small and it’s the unmanaged resources that are most concerning and account for approximately 1MB to 2MB for each object instance!  Omitting to explicitly call Dispose() means the .NET (non-deterministic) garbage collector gets out of sync with the finalizer and the unmanaged memory does not get reclaimed in a timely manner possibly blocking future memory allocations. For further reading I recommend reviewing Stefan Goßner’s blog Dealing with Memory Pressure problems in MOSS/WSS .

The unmanaged memory leaks can grow very quickly especially when traversing through frequently called areas like site navigation code and item event receivers.  The lack of proper Dispose() hygiene can increase your risk of frequent IIS Application Domain recycles (see Steve Sheppard’s blog Overlapped Recycling And SharePoint: Why SharePoint Requires It), Out Of Memory (OOM) exceptions, high memory consumption, and poor performing SharePoint production environments.

To Dispose or not Dispose?! That is the question…

To make matters more confusing for SharePoint developers there are times when SPSite and SPWeb objects should not be disposed and are cleaned up by SharePoint and ASP.NET after page processing is completed.  In addition, there are cases when developers indirectly call a property on a object that creates and holds an internal reference to a SPSite or SPWeb object (for example SPSite.ParentWeb property).  Understanding the origin and the scope the object was created is paramount when determining whether or not to explicitly call dispose.

Dispose Patterns

When writing customized SharePoint code you need to be aware of the scope and context of each SPSite, SPWeb objects lifetime.  When objects are created and destroyed in the same method or iteration scope (foreach or do/while loop) they are the easiest to clean handle.  Things become more complex to review when developers create objects in one method and dispose in another.  Areas to be aware of are assigning objects to class variables or static/global variables which may hold on to the object reference across method calls.  The content in this blog post is a combination of Product Group Guidance along with any edge cases that have been discovered in the field through support incidents.


Try / finally using() Dispose()

Make certain that the objects listed here which need to be properly Disposed by your application code is wrapped within a using(), try/finally, or try/catch/finally block.  Failure to do so can lead to unexpected memory leaks as there is no other way to guarantee that your Dispose will be reached in the event of an exception.  See my updated guidance here.

Troubleshooting Dispose Related Leaks

Recently the SPDisposeCheck utility was announced by Paul Andrew from the Microsoft SharePoint Product Group which was published 1/29/09 here.  This command line utility will help scan your custom MOSS and WSS 3.0 .NET assemblies (MSIL not your original source code) automatically scanning for the guidance listed on this blog post.  SPDisposeCheck will produce a summary report calling your attention to areas in your code you that you need to closely examine for possible Dispose() related leaks.  After you have completed your initial scan with SPDisposeCheck you should examine the SharePoint ULS logs to help further verify that no Dispose() edge cases will be introduced into your shared development, test, and production environments.  Stefan Goßner (Microsoft Support Escalation Engineer) has created guidance on using the ULS logs to Troubleshooting SPSite/SPWeb leaks in WSS v3 and MOSS 2007 .  See also Stefan’s Disposing SPWeb and SPSite objects for additional info.  For advanced troubleshooting you can ($) contact Microsoft Support or if you have a Premier Contract you can engage Microsoft Premier Support’s Developer Advisory Services (DAS) or Premier Field Engineering (PFE) resources for advanced troubleshooting, debugging, and proactive advisory services including custom code reviews.

SharePoint Memory Internals

400 Level – (Optional read) It’s very important for the developer of the application code to understand when is the optimal time for code optimization and performance reasons to hold on to and release the proper SharePoint objects in a timely fashion.  As SharePoint Application developers you should be familiar with the public SPSite and SPWeb objects but internally to get their work done the real managed object that holds onto the unmanaged heap is SPRequestInternalClass which is internal to a wrapper class SPRequest.

Internally we use a RCW (runtime callable wrapper) which is essentially a finalizable object (there are subtle differences but they don’t really come into play here).    If that object is no longer rooted, the finalizer (teardown of the RCW) will release the native memory.   However, like normal finalizers the RCW teardown is performed by a single finalizer thread which generally can’t keep up with cleaning these objects if they’re being leaked many times per second.

Not releasing the SharePoint objects in a timely fashion can lead to poor memory hygiene including excessive fragmentation, pinning, and OOM exceptions building very quickly.  Problematic code which fails to properly dispose objects in a timely fashion becomes exacerbated especially in x32bit environments with a large number of sites.  We encourage all Enterprise SharePoint farms to use x64bit versions of the OS and MOSS to take advantage of the additional addressable memory.

Note resources other than just memory are effected by not properly disposing your object in a timely fashion.  For example, SQL Server establishes a 1:1 connection with each SPRequest object and it lives up to the release of the internal SPRequest object (which is used by your SPSite and SPWeb object).

Microsoft.SharePoint.SPList

  • SPList.BreakRoleInheritance() method no longer requires list.ParentWeb.Dispose() to be executed after calling BreakRoleInheritance() due to the updated ParentWeb guidance (see ParentWeb) later in this blog post.

Microsoft.SharePoint.SPSite

  • RootWeb property no longer requires Dispose() to be called on itself as previously indicated in the Whitepaper Best Practices: Using Disposable Windows SharePoint Services Objects .  See my blog post here for more detail.  In addition, properties LockIssue, Owner, and SecondaryContact internally used the RootWeb property.  Based on the updated Microsoft guidance for RootWeb there is no longer a requirement to explicitly Dispose on any of these properties.  Note that the owning SPSite object must be properly Disposed (or not Disposed in the case of SPContext) as described by the rules listed elsewhere in this blog.

  • new SPSite() operator – Instantiating SPSite objects with the new operator needs to be disposed.

Note: With C# you can automatically have the Dispose() called for you when the object leaves the scope by wrapping the code with the using() { } statement.

void CreatingSPSiteLeak()
{
    SPSite siteCollection = new SPSite("http://moss");
    // siteCollection leaked
}

void CreatingSPSiteExplicitDisposeNoLeak()
{
    SPSite siteCollection = null;
    try
    {
        siteCollection = new SPSite("http://moss");
    }
    finally
    {
        if (siteCollection != null)
            siteCollection.Dispose();
    }
}

CreatingSPSiteWithAutomaticDisposeNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
    } // SPSite object siteCollection.Dispose() automatically called
}

Avoid the following pattern which collapses SPSite and SPWeb calls.  The example returns the SPWeb site object wrapped by a using statement which gets disposed but there is no way to dispose the underlying SPSite object.

void OpenWebLeak()
{
    using (SPWeb web = new SPSite(SPContext.Current.Web.Url).OpenWeb())
    {
        // SPSite leaked !
    } // SPWeb object web.Dispose() automatically called
}

void OpenWebNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
        } // SPWeb object web.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called
}

  • AllWebs[] Indexer returns SPWeb object that needs to be disposed to avoid aggregation of memory which can lead to memory pressure when running on a site collection with large number of sub sites.
void AllWebsForEachLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            foreach (SPWeb innerWeb in siteCollection.AllWebs)
            {
                // explicit dispose here to avoid OOM's with large # of webs
            }
        } // SPWeb object outerWeb.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called 
}

void AllWebsForEachNoLeakOrMemoryOOM()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            foreach (SPWeb innerWeb in siteCollection.AllWebs)
            {
                try
                {
                    // ...
                }
                finally
                {
                    if(innerWeb != null)
                        innerWeb.Dispose();
                }
            }
        } // SPWeb object outerWeb.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called 
}

void AllWebsIndexerLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        SPWeb web = siteCollection.AllWebs[0];
        // SPWeb web leaked
    }  // SPSite object siteCollection.Dispose() automatically called 
}

void AllWebsIndexerNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.AllWebs[0])
        {
        } // SPWeb object web.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called 
}

void AllWebsAddLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        SPWeb web = siteCollection.AllWebs.Add("site-relative URL");
        // SPWeb web Leaked
    }  // SPSite object siteCollection.Dispose() automatically called 
}

void AllWebsAddNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.AllWebs.Add("site-relative URL"))
        {
        } // SPWeb object web.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called 
}

Microsoft.SharePoint.SPWeb

void SPLimitedWebPartManagerLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            SPFile page = web.GetFile("Source_Folder_Name/Source_Page");
            SPLimitedWebPartManager webPartManager = page.GetLimitedWebPartManager(PersonalizationScope.Shared);
            // SPWeb object webPartManager.Web leaked
        } // SPWeb object web.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called 
}

void SPLimitedWebPartManagerNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            SPFile page = web.GetFile("Source_Folder_Name/Source_Page");
            using (SPLimitedWebPartManager webPartManager = page.GetLimitedWebPartManager(PersonalizationScope.Shared))
            {
                try
                {
                    // ...
                }
                finally
                {
                    webPartManager.Web.Dispose();
                }
            }
        } // SPWeb object web.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called 
}
 

void WebsLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            foreach (SPWeb innerWeb in outerWeb.Webs)
            {
                // SPWeb innerWeb leak
            }
        } // SPWeb object outerWeb.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called 
}

void WebsNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            foreach (SPWeb innerWeb in outerWeb.Webs)
            {
                try //should be 1st statement after foreach
                {
                    // ...
                }
                finally
                {
                    if(innerWeb != null)
                        innerWeb.Dispose();
                }
            }
        } // SPWeb object outerWeb.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called 
}

  • SPWeb.Webs.Add() method returns a SPWeb object that needs to be disposed.
void WebsAddLeak(string strWebUrl)
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            SPWeb addedWeb = web.Webs.Add(strWebUrl);   // will leak

        } // SPWeb object web.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called
}

void WebsAddNoLeak(string strWebUrl)
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            using (SPWeb addedWeb = web.Webs.Add(strWebUrl))
            {
                //..
            }

        } // SPWeb object web.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called
}

Microsoft.SharePoint.SPWebCollection

void SPWebCollectionAddLeak(string strWebUrl)
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            SPWebCollection webCollection = siteCollection.AllWebs; // no AllWebs leak just getting reference
            SPWeb innerWeb = webCollection.Add(strWebUrl);  // must dispose of innerWeb
            // innerWeb Leak
        } // SPWeb object outerWeb.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called 
}

void SPWebCollectionAddNoLeak(string strWebUrl)
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            SPWebCollection webCollection = siteCollection.AllWebs; // no AllWebs leak just getting reference
            using (SPWeb innerWeb = webCollection.Add(strWebUrl))
            {
                //...
            }
        } // SPWeb object outerWeb.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called 
}

Microsoft.SharePoint.WebControls.SPControl

void SPControlBADPractice()
{
    SPSite siteCollection = SPControl.GetContextSite(Context);
    siteCollection.Dispose();   // DO NOT DO THIS
    SPWeb web = SPControl.GetContextWeb(Context);
    web.Dispose();  // DO NOT DO THIS
}

void SPControlBestPractice()
{
    SPSite siteCollection = SPControl.GetContextSite(Context);
    SPWeb web = SPControl.GetContextWeb(Context);
    // Do NOT call Dispose()
}

Microsoft.SharePoint.SPContext

void SPContextBADPractice()
{
    SPSite siteCollection = SPContext.Current.Site;
    siteCollection.Dispose(); // DO NOT DO THIS
    SPWeb web = SPContext.Current.Web;
    web.Dispose(); // DO NOT DO THIS
}

void SPContextBestPractice()
{
    SPSite siteCollection = SPContext.Current.Site;
    SPWeb web = SPContext.Current.Web;
    // Do NOT call Dispose()
}

Microsoft.SharePoint.Publishing

void PublishingWebCollectionLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            // passing in web you own, no dispose needed on outerPubWeb
            PublishingWeb outerPubWeb = PublishingWeb.GetPublishingWeb(web);

            PublishingWebCollection pubWebCollection = outerPubWeb.GetPublishingWebs();
            foreach (PublishingWeb innerPubWeb in pubWebCollection)
            {
                // innerPubWeb leak
            }
            // PublishingWeb will leak for each innerPubWeb referenced
        } // SPWeb object web.Dispose() automatically called
    } // SPSite object siteCollection.Dispose() automatically called
}

void PublishingWebCollectionNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            // passing in web you own, no dispose needed on outerPubWeb
            PublishingWeb outerPubWeb = PublishingWeb.GetPublishingWeb(web);
            PublishingWebCollection pubWebCollection = outerPubWeb.GetPublishingWebs();
            foreach (PublishingWeb innerPubWeb in pubWebCollection)
            {
                try
                {
                    // ...
                }
                finally
                {
                    if(innerPubWeb != null)
                        innerPubWeb.Close();
                }
            }
            // outerPubWeb.Close(); not needed and if called will log warning in ULS log
        }  // SPWeb object web.Dispose() automatically called
    } // SPSite object siteCollection.Dispose() automatically called
}

void GetPublishingWebNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            // passing in web you own, no dispose needed on singlePubWeb
            PublishingWeb singlePubWeb = PublishingWeb.GetPublishingWeb(web);
        } // SPWeb object web.Dispose() automatically called
    } // SPSite object siteCollection.Dispose() automatically called
}

void GetVariationLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);  // Passing in web so no Close() needed
            VariationLabel variationLabel = Variations.Current.UserAccessibleLabels[0];
            PublishingWeb variationPublishingWeb = publishingWeb.GetVariation(variationLabel);  // must be Closed()
            // ...
        } // SPWeb object web.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called 
}

void GetVariationNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            PublishingWeb variationPublishingWeb = null;
            try
            {
                PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);  // Passing in web so no Close() needed
                VariationLabel variationLabel = Variations.Current.UserAccessibleLabels[0];
                variationPublishingWeb = publishingWeb.GetVariation(variationLabel);  // must be Closed()
                // ...
            }
            finally
            {
                if(variationPublishingWeb != null)
                    variationPublishingWeb.Close();
            }
        } // SPWeb object outerWeb.Dispose() automatically called
    }  // SPSite object siteCollection.Dispose() automatically called 
}

Microsoft.Office.Server.UserProfiles

UserProfiles.PersonalSite requires a Dispose() call before leaving scope if you use the property in your code.  For addition code optimization information please see my blog post here.

void PersonalSiteLeak()
{
    // open a site collection
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(siteCollection));
        UserProfile profile = profileManager.GetUserProfile("domain\\username");
        SPSite personalSite = profile.PersonalSite;    // will leak
    }
}

void PersonalSiteNoLeak()
{
    // open a site collection
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(siteCollection));
        UserProfile profile = profileManager.GetUserProfile("domain\\username");
        using (SPSite personalSite = profile.PersonalSite)
        {
            // ...
        }
    }
}

Microsoft.SharePoint.Administration

void SPSiteCollectionIndexerLeak()
{
    using (SPSite siteCollectionOuter = new SPSite("http://moss"))
    {
        SPWebApplication webApp = siteCollectionOuter.WebApplication;
        SPSiteCollection siteCollections = webApp.Sites;

        SPSite siteCollectionInner = siteCollections[0];
        // SPSite siteCollectionInner leak 
    } // SPSite object siteCollectionOuter.Dispose() automatically called
}

void SPSiteCollectionIndexerNoLeak()
{
    using (SPSite siteCollectionOuter = new SPSite("http://moss"))
    {
        SPSite siteCollectionInner = null;
        try
        {
            SPWebApplication webApp = siteCollectionOuter.WebApplication;
            SPSiteCollection siteCollections = webApp.Sites;

            siteCollectionInner = siteCollections[0];
        }
        finally
        {
            if (siteCollectionInner != null)
                siteCollectionInner.Dispose();
        }
    } // SPSite object siteCollectionOuter.Dispose() automatically called
}

void SPSiteCollectionForEachLeak()
{
    using (SPSite siteCollectionOuter = new SPSite("http://moss"))
    {
        SPWebApplication webApp = siteCollectionOuter.WebApplication;
        SPSiteCollection siteCollections = webApp.Sites;

        foreach (SPSite siteCollectionInner in siteCollections)
        {
            // SPSite siteCollectionInner leak
        }
    } // SPSite object siteCollectionOuter.Dispose() automatically called
}

void SPSiteCollectionForEachNoLeak()
{
    using (SPSite siteCollectionOuter = new SPSite("http://moss"))
    {
        SPWebApplication webApp = siteCollectionOuter.WebApplication;
        SPSiteCollection siteCollections = webApp.Sites;

        foreach (SPSite siteCollectionInner in siteCollections)
        {
            try
            {
                // ...
            }
            finally
            {
                if(siteCollectionInner != null)
                    siteCollectionInner.Dispose();
            }
        }
    } // SPSite object siteCollectionOuter.Dispose() automatically called
}

  • Add() returns a SPSite object which needs to be disposed.
void SPSiteCollectionAddLeak()
{
    SPWebApplication webApp = new SPSite("http://moss").WebApplication;
    SPSiteCollection siteCollections = webApp.Sites;
    SPSite siteCollection = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\\User", "roger.lamb@litwareinc.com");
    // SPSite siteCollection leak
}

void SPSiteCollectionAddNoLeak()
{
    SPWebApplication webApp = new SPSite("http://moss").WebApplication;
    SPSiteCollection siteCollections = webApp.Sites;
    using (SPSite siteCollection = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\\User", "roger.lamb@litwareinc.com"))
    {
    } // SPSite object siteCollection.Dispose() automatically called
}

Microsoft.SharePoint.Portal.SiteData.Area (Obsolete)

  • Area.Web property creates a SPWeb object that needs to be Disposed().  Although the Area class is obsolete in MOSS 2007 it is still of concern when migrating legacy code.
public void AreaWebLeak()
{
    // AreaManager and Area are obsolete in MOSS but this should still be noted
    Area area = AreaManager.GetArea(PortalContext.Current, new Guid("{GUID}"));
    SPWeb areaWeb = area.Web;   // this SPWeb object must be disposed
    string str = areaWeb.Title;
    // SPWeb areaWeb leak
}

public void AreaWebNoLeak()
{
    // AreaManager and Area are obsolete in MOSS but this should still be noted
    Area area = AreaManager.GetArea(PortalContext.Current, new Guid("{GUID}"));
    using (SPWeb areaWeb = area.Web)
    {
        string str = areaWeb.Title;
    }
}

Cross Method Dispose Patterns

  • The following example demonstrates the common practice of holding onto the SPSite and SPWeb objects across methods in a class.  There are times where this design pattern is required however make sure you don’t overlook the appropriate time to call dispose when you are finished with the cross method calls.  Below is an example of this pattern and shows and example of a leak of both SPSite and SPWeb when the class is torn down.
public class CrossMethodLeak
{
    private SPSite _siteCollection = null;
    private SPWeb _web = null;

    public void MethodA()
    {
        _siteCollection = new SPSite("http://moss");
        _web = _siteCollection.OpenWeb();
    }

    public void MethodB()
    {
        if (_web != null)
        {
            string title = _web.Title;
        }
    }

    public void MethodC()
    {
        if (_web != null)
        {
            string name = _web.Name;
        }
    }
}

Summary

If you extended SharePoint with custom code you should digest this post carefully to avoid expensive consequences commonly found in production environments.  For a more verbose explanation of many of the topics covered in this blog post I suggest you read the MSDN White Papers Best Practices: Using Disposable Windows SharePoint Services Objects and Best Practices: Common Coding Issues When Using the SharePoint Object Model .

Special thanks to my Microsoft colleagues on the SPDisposeCheck Core Development team: Sean Thompson, Greg Varveris, and Paul Andrew

Thanks to the many reviewers and contributors including Stefan Goßner, Steve Sheppard, Lisa Guthrie, Cliff Green, and Rick Caudle.


Comments (73)

  1. Sharepoint SharePoint 2007 and WSS 3.0 Dispose Patterns by Example [Via: Roger Lamb ] WPF WPF 3.5 Data…

  2. Don Smith says:

    My good friend Roger Lamb has just joined the blogsphere and I assure you that if you are interested

  3. arangas says:

    Hi Roger,

    This article is a good reference as it covers more than the MSDN best practices article. It would be great to see this post updated as you think of more information. I noticed that you don’t mention (not) disposing when using SPContext, which would be a good addition (and what about cases such as SPContext.Current.Site.RootWeb?). Using the ULS logs to find which parts of your code aren’t disposing correctly would be great too.

    Thanks again, Alex.

  4. For his first post he has provided a bunch of examples of dispose patterns for SharePoint that will be

  5. For his first post he has provided a bunch of examples of dispose patterns for SharePoint that will be

  6. When developing for the SharePoint platform, developers should be very careful in using the SharePoint

  7. Very good job detailing a lot more the default principles on disposing objects.

    Maxime

  8. Un post qui vient un poil en doublon de ceux qui relaye 2 nouveaux articles de blog mais ces posts sont

  9. Hacía tiempo que no revisaba mis RSS sobre SharePoint , y claro me ha costado ponerme al día, y aquí

  10. A very good article about Sharepoint 2007 and WSS 3.0 Dispose Patterns: Roger Lamb's SharePoint Developer

  11. bazztrap says:

    Basically any new instance has to be disposed, I am curious about objects which are referenced from different events like  EvenHandler properties properties.OpenWeb()  

  12. Tipps Sending alerts to groups in Sharepoint 2007 SharePoint 2007 Permissions Matrix What is ReGhost

  13. Back in February, Roger Lamb kicked off his MSDN blog with a great post ( SharePoint 2007 and WSS 3.0

  14. rlasker3 says:

    I also want to know about the SPFeature.Parent. I have seen arguments either way and would prefer something a little definitive.

  15. SaurabhKV says:

    When writing customized SharePoint code you need to be aware of the scope and context of each SPSite

  16. Microsoft.SharePoint.SPList.BreakRoleInheritance.

    Consider this:

    To get a SPList object, you need to have a SPWeb object (is there any other way?), wouldn’t that be the same (reference as well) as the SPList.ParentWeb object.  Now if this SPWeb object came from the current context then it should not be disposed.  If the SPWeb object was not created by the current context then it should be disposed.

    using (SPSite mySite = new SPSite("http://server"))

    using (SPWeb myWeb = mySite.OpenWeb())

    {

         SPList myList = myWeb.Lists[0];

         // is myList.ParentWeb always the same reference as myWeb

         // if not – what circumstances is it not.

         // if so – parentweb is disposed with the using statement

    }

    Are we sure we need to dispose SPList.ParentWeb ???

    Is this correct ??

  17. Great article, Robert! Thank you.

    What do you think should be the best way to handle a situation like this:

    using (SPSite mySite = new SPSite("http://server"))

    using (SPWeb myWeb = mySite.OpenWeb())

    {

         myMethod(myWeb);

    }

    private void myMethod(SPWeb web)

    {

         // work with the web object

        // should dispose be called? What about the reference copy on the stack?

    }

    And situation 2:

    private void myMethod(SPWeb web)

    {

         // work with the web object

        mySite.OpenWeb()) // then open another web….

        // call dispose.

    }

    Do you agree? Or can you suggest another pattern to avoid these situations?

    Thanks

  18. Après vous avoir parlé de ce que l’on pouvait faire avec les extensions de méthodes et un peu de réflection,

  19. Stefan Gossner has published a great article on the subject: In an earlier article I have discussed that

  20. sergioko says:

    Does MS have a plan to close any of this leaks like in BreakRoleInheritance() internally? What happens to our perfect existing code, if next  MOSS/WSS service pack will invent a proper dispose of ParentWeb in there? Double disposal?

  21. Puesta en situación En uno de nuestros proyectos hemos tenido que manejar el contexto del usuario en

  22. First Hand says:

    Источник…Смысл в том, чтобы уничтожать объекты SPweb, SPSite и т.п. методом Dispose() принудительно….

  23. ajayrsawant says:

    We have code where at times we’ll be returning an SPWeb object from a function. So for example:

    public SPWeb getDeptWeb()

    {

    SPWeb deptWeb = SpSite.OpenWeb(SpContext.Web.ID);

    return deptWeb;

    }

    How can we dispose of the SPWeb object in this instance? Or is it sufficient to dispose it where we’re accepting the returned parameter?

  24. Beim Programmieren im SharePoint-Umfeld sollte man großen Wert auf saubere und sicherer Programmierung

  25. Maailmas on palju blogijaid ja nende heade ning väärtuslike postituste leidmine ei ole alati lihtne ega

  26. miguel.isidoro@create.pt says:

    Hello Roger,

    A few remarks on your post. I think it is great and covers a large amount of situations. Two types of situations I noticed are not covered:

    Workflows:

    workflowProperties.Web, workflowProperties.Site and workflowProperties.Item.Web don’t need to be disposed.

    Event handlers:

    properties.ListItem.Web

    properties.ListItem.Web.Site

    About these two, I am not sure if they need to be disposed. Do you know if these need to be disposed?

    Thanks

    Miguel

  27. Might as well dive right in and make the inaugural posting useful… Anyone who's done some development

  28. SharePoint says:

    The URL given below takes you to an very useful article which describes the appropriate ways to write your code when using Microsoft Windows SharePoint Services objects so that you can avoid retaining the objects in memory with the Microsoft .NET Framework.

  29. Performance is perspective that all the developers forget during development and it pops up and the end

  30. He encontrado esta página donde nos explican la manera correcta de utilizar el Dispose() cuando accedemos

  31. Sven De Bont says:

    Excellent article!

    Got us through a very annoying ‘OutOfMemeryExcepton’ when modifying a webpart page in 6000 childwebs (the culprit was the SPLimitedWebPartManager)

  32. I am writing this post by inspired by couple of performance related cases that I had been worked with

  33. Disposal of objects in WSS and SharePoint 2007

  34. Recently I was faced with the problem of finding an ancestor web (SPWeb object) of the current web. The

  35. SPDisposeCheck tool officially announced Last year I leveraged a good friend and coworker (Roger Lamb)

  36. I was posting a WPF question on stackoverflow and I took a few minutes to tie up loose ends on a question

  37. SPSite and SPWeb implement the IDisposable interface

  38. Paul Andrew says:

    SPDisposeCheck is a tool to help SharePoint Developers follow memory management best practices when using

  39. Blog Tech says:

    Outil : SPDisposeCheck est disponible

  40. Tal y como nos comenta Paul Andrew en este post , Microsoft acaba de liberar en MSDN Code Gallery la

  41. [via Paul Andrew ] SPDisposeCheck avait été présenté dernièrement sur le blog de l’équipe SharePoint

  42. m_3ryan@hotmail.com says:

    I thinks the best practice on this article is to use the object Microsoft.SharePoint.WebControls.SPControl and get rid of thinking about disposing again.

  43. Today the Microsoft SharePoint Product Team announced the SPDisposeCheck utility here and also at Paul

  44. Body: I've been eagerly awaiting the public release of the SP Dispose Check Tool and now its available

  45. SPDisposeCheck jest już od paru dni dostępny , i muszę powiedzieć, że tool mi się podoba – choć mam wrażenie

  46. New Tech Meetup Birthday & Pecha Kucha Night MS Rampup – SharePoint for Developers Track –

  47. Hay una cosa que tenemos que tener clara y es que Sharepoint no deja de ser un producto más o menos cerrado

  48. SPDisposeCheck jest już od paru dni dostępny , i muszę powiedzieć, że tool mi się podoba – choć mam wrażenie

  49. I’m the project co-ordinator for the SharePoint Learning Kit at http://www.codeplex.com/slk. As it’s a great tool, I’d love to use SPDisposeCheck on it, but it checks to see if the file name begins with Microsoft (it does) and then if I rename the file, if any namespace begins with Microsoft (they all do) and then skips the assembly. Is there any possibility of adding an option to not skip Microsoft assemblies. And before you ask, we can’t change the namespaces as it was originally a Microsoft project and it would break all existing installations.

    Thanks,

    Richard

  50. TechDays 2009 – Enhancing the SharePoint developer experience – Supporting links

  51. sergioko says:

    Hello Roger,

    One more situation I noticed, when disposing does bad, and not disposing is probably bad too 😉

    In CreateChildControl() method of a webpart, I use this pattern to render a field of a list item from another web:

    using (SPSite site = new SPSite("http://address"))

    using (SPWeb web = site.OpenWeb())

    {

                 BaseFieldControl c = field.FieldRenderingControl;

                 c.ItemContext = SPContext.GetContext(HttpContext.Current, item.ID, list.ID, web);

                 Controls.Add(c);

    }

    The problem is that the BaseFieldControl keeps a reference of the web object (through the ItemContext) and at its rendering stage the web has already been invalidated and exception says "List does not exist The page you selected contains a list that does not exist. It may have been deleted by another user.."

    If I don’t dispose site and web objects with that ‘using’ directive, everything works fine, but I’m afraid the memory going to leak somewhere… How can I control the disposing of context objects like this?

  52. Last Saturday, another team member sent an email out to the team inquiring about the "MOSS object disposal

  53. [via Stephen Vick’s Blog ] Dans la série des nouvelles qui ne font pas plaisir, il semblerait que SPDisposeCheck

  54. Dinesh Bolkensteyn says:

    Hi Roger,

    It would be cool if SPDisposeCheck could not only check for memory leaks, but also for invalid disposes (if case someone tries to free something that is part of the automagically SharePoint framework managed objects)

    Of course there’s SPContext, but from what Miguel Isidoro (see previous comment) said, it also includes workflows and list event handlers, and maybe even more.

    Many thanks !

  55. Hi Dinesh, I agree.. we are reviewing adding this feature to the approved list of ‘do not dispose’ mentioned in the msdn whitepaper and my blog.

  56. [English version @ http://sovfxcoprules.codeplex.com ] Après : SharePointOfView , qui regroupe un ensemble

  57. mpeyer says:

    Hi Roger

    I couldn’t find an advice on how to deal with the Workflow Context object. I’m developing a custom SPD workflow action and there I retrieve the DependenyProperty __Context which is of type Microsoft.SharePoint.WorkflowActions.WorkflowContext. This object has a parameter Site and also Web and implements IDisplosable. Now I’m wondering if I have to dispose this WorkflowContext object by myself or not.

    Regards, Marc

  58. arangas says:

    Roger, this might be an obvious one but what about SPWeb.Site to retrieve the current site collection for a web? It looks to me like this does not need to be disposed.

    Thanks Alex.

  59. arangas says:

    Also, using the enumerator in SPListCollection causes a memory leak to be logged:

    This SPRequest was allocated at    at Microsoft.SharePoint.Library.SPRequest..ctor()     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.SPListCollection.EnsureListsData(Guid webId, String strListName)     at Microsoft.SharePoint.SPListCollection.EnsureListsData(String strListName)     at Microsoft.SharePoint.SPListCollection.Undirty()     at Microsoft.SharePoint.SPBaseCollection.System.Collections.IEnumerable.GetEnumerator()

  60. jarofkla says:

    So what is the proper way to dispose in your Cross Method example?  I’ve tried to use a class destructor to manage the dispose of the web and site, but it doesn’t seem to work.

  61. This is a great resource, as is the SPDisposeChecker tool. It appears you missed out SiteAdministrationSelector.CurrentItem though as well as disposing of streams that come of SPFile.

    For details see

    http://www.muhimbi.com/blog/2009/08/additional-little-known-sharepoint.html

    and

    http://www.muhimbi.com/blog/2009/07/solution-for-sharepoints-common.html

  62. mikeycooper says:

    The static methoc SPList.Exists seems to leak an SPSite.  It uses "new SPSite" but then does not dispose of it.

  63. mikeycooper says:

    Sorry, that should be "The static method SPSite.Exists seems to leak an SPSite."

  64. StephenDVick says:

    To see how to run SPDisposeCheck as a build task and automated unit test that can be applied to a check in policy see:

    http://stephenvick.wordpress.com/2010/01/06/run-spdisposecheck-as-build-task-and-automated-unit-test/

    -Vick

  65. cglessner says:

    Hi Roger,

    are you really sure that you have to dispose a reference to a SPWeb explicitly? My research (reflection) shows that the SPSite tracks all SPWeb tha it has been emitted in an internal list. And when you call Dispose on the SPSite, it loops through all this SPWebs and call Close() on each. The SPWeb.Dispose() method does nothing else than calling it’s Close() method. So it seems to me that is not necessary to call SPWeb.Dispose() explicitly as long as you Dispose the SPSite. Sure there are scenarios where explicit disposing make sense but it seems not to be a must. Read more http://cglessner.blogspot.com/2010/03/sharepoint-disposing-myths.html

    Bye, Christian

  66. Shai says:

    Your tool reports "Dispose/Close was not called on SPLimitedWebPartManager.Web" wrongly.

    If I assign a value to it in CreateChildContorls and dispose of it in Render – it reports it was not disposed of.

    It shoudl check if it was disposed later.

    As general note: this is a nice tool for developers – I am sorry to see it is misused by sys administrators as a validation tool. You should make it 100% that this tool is not accurate, and does not report memory leaks – only possible leaks. In other words – EVERY THING HERE is a false positive. I can dispose of objects in many ways that this tool does not detect.

  67. Very good article.  It is very helpfull to me. Now i will write my code with the correct form using these patterns!

    Thank you!

  68. this is a post you can always refer