共用方式為


Disposing Objects

Applies to: SharePoint Foundation 2010

Introduction to Using Disposable SharePoint Objects

The objects in the Microsoft SharePoint Foundation 2010 and Microsoft SharePoint Server 2010 object models serve as an interface for working with SharePoint Foundation data. Frequently, developers call into the object model to read data from or write new data to the SharePoint Foundation 2010 and SharePoint Server 2010 data stores.

The SharePoint Foundation 2010 and SharePoint Server 2010 object models contain objects that implement the IDisposable interface. You must take precautions when using these objects to avoid their long-term retention in memory in the Microsoft .NET Framework.

Specifically, you should explicitly dispose those SharePoint objects that implement IDisposable when you are finished using them.

In scenarios in which you use SharePoint objects extensively—for example, in SharePoint sites that use custom Web Parts—you can cause the following unusual behaviors by not disposing of SharePoint objects when you are finished with them.

  • Frequent recycles of the SharePoint Foundation application pool, especially during peak usage

  • Application crashes that appear as heap corruption in the debugger

  • High memory use for Internet Information Services (IIS) worker processes

  • Poor system and application performance

This article serves as a guide to the proper procedures for handling and disposing of SharePoint objects that implement IDispose. The issues discussed in this article are also flagged by the SharePoint Dispose Checker Tool, a free program available as a download that inspects your assemblies for coding practices that cause memory leaks because of improper handling and disposal of SharePoint objects.

Why Dispose?

Several of the SharePoint Foundation 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 SharePoint Foundation must dispose the objects when the applications finish using them. You should not rely on the garbage collector to release them from memory automatically.

Finding Incorrectly Disposed Objects

You can identify the potential presence of incorrectly disposed objects by asking the following questions:

  1. Does your application pool recycle frequently, especially under heavy loads (assuming that the application pool is set to recycle when a memory threshold is reached)?

    The memory threshold should be 800 megabytes (MB) to 1.5 gigabytes (GB), assuming at least 2 GB of RAM. Setting the recycle of the application pool to occur closer to 1 GB gives the best results, but experiment to determine what settings work best for your environment. If the recycle setting is too low, your system experiences performance issues because of frequent application pool recycles. If the setting is too high, your system experiences performance problems because of page swapping, memory fragmentation, and other issues.

  2. Does your system perform poorly, especially under heavy loads?

    As memory usage begins to increase, the system must compensate, for example, by paging memory and handling memory fragmentation.

  3. Does your system crash or do users experience unexpected errors such as timeouts or page-not-available errors, especially under heavy loads?

    Again, when memory usage increases or gets fragmented, some functions fail because they cannot allocate memory for other operations. In many cases, the code does not properly handle the "out of memory" exception, which leads to false or misleading errors.

  4. Does your system use custom or third-party Web Parts or custom applications?

    You might not be aware that these Web Parts must dispose SharePoint objects and why, assuming that garbage collection performs this function automatically. However, that is not true in all cases.

If you answer "yes" to number 4, and to one or more of the other questions, there is a good chance that your custom code is not disposing of items properly.

If your sites are displaying any of the unusual behaviors described previously, you can determine whether the cause is a memory leak due to incorrectly disposed objects by checking the ULS logs (available at C:\Program Files\Common Files\microsoft shared\Web Server Extensions\14\LOGS) for entries related to the SPRequest object. Each instance of SPSite and SPWeb contains a reference to an SPRequest object that, in turn, contains a reference to an unmanaged COM object that handles communications with the database server. SharePoint Foundation monitors the number of SPRequest objects that exist in each specific thread and in parallel threads, and adds useful entries to the logs under the three following scenarios:

  • The total number of SPRequest objects exceeds a configurable threshold.

  • An SPRequest object continues to exist at the end of a thread.

  • An SPRequest object was removed from the heap by garbage collection

The first scenario occurs most frequently, especially if your site uses the default threshold value of eight SPRequest objects. Whenever the number of SPRequest objects exceeds this threshold, the following entry appears in the ULS logs:

"Potentially excessive number of SPRequest objects (number of objects) currently unreleased on thread number of thread. Ensure that this object or its parent (such as an SPWeb or SPSite object) is being properly disposed. Allocation Id for this object: {GUID}"

The best threshold varies according to the nature of your site and the applications running on it. When your sites are experiencing problems with performance, you should monitor your installation's ULS logs to understand how many SPRequest objects your site applications are creating. This helps you determine whether the designs of your sites and applications are creating too many SPRequest objects. Even if incorrect disposal of objects is not the cause of your performance problem, you might need to redesign your sites or custom site applications to reduce overall memory consumption caused by excessive proliferation of SPRequest objects.

Because the very low default threshold may not apply to many sites, you can change this threshold by editing the following registry subkey:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings

LocalSPRequestWarnCount = desired threshold value

After you determine that incorrect disposal of objects might be causing SPRequest objects to proliferate and unnecessarily increase the memory footprint of your sites, you can find specific instances of incorrect disposal by looking for the following two entries. Both messages point to cases where memory is being wasted because of incorrect disposal of SharePoint objects, and both relate to the number and state of SPRequest objects on a single thread:

  • "An SPRequest object was not disposed before the end of this thread. To avoid wasting system resources, dispose this object or its parent (such as an SPSite or SPWeb) as soon as you are done using it. This object will now be disposed. Allocation Id: {GUID}To determine where this object was allocated, create a registry subkey at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings. Then create a new DWORD named SPRequestStackTrace with the value 1 under this key."

    This message indicates that an SPRequest object was disposed because it still existed at the end of a thread.

  • "An SPRequest object was reclaimed by the garbage collector instead of being explicitly freed. To avoid wasting system resources, dispose this object or its parent (such as an SPSite or SPWeb) as soon as you are done using it. Allocation Id: {GUID} To determine where this object was allocated, create a registry key at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings. Then create a new DWORD named SPRequestStackTrace with the value 1 under this key."

    This message indicates that the garbage collector disposed an SPRequest object.

To identify the code that causes the problem, you can search in the logs for entries that contain the allocation identifiers, or follow the instructions in the warnings and add the following subkey setting to the registry:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings SPRequestStackTrace = 1

This subkey setting ensures that the stack trace of the original SPRequest allocation (which occurs whenever an SPSite or SPWeb object is created) is added to the logs when these warnings occur.

The following sections describe several coding techniques you can use to ensure that the objects are disposed properly.

Coding Techniques to Ensure Object Disposal

You can employ certain coding techniques to ensure object disposal. These techniques include using the following in your code:

  • The Dispose method

  • The using statement

  • try, catch, and finally blocks

Dispose vs. Close Method Usage

The Dispose and Close methods for the SPWeb object and SPSite object function in the same way. The Dispose method calls the object's Close method. We recommend calling the Dispose method, instead of Close, because SPWeb and SPSite objects implement the IDisposable interface, and standard .NET Framework garbage collection calls the Dispose method to free any resources associated with the object from memory.

The using Statement

You can automatically dispose SharePoint objects that implement the IDisposable interface by using the Microsoft Visual C# and Visual Basic using statement.

The following code provides an example.

String str;

using(SPSite oSPsite = new SPSite("https://server"))
{
  using(SPWeb oSPWeb = oSPSite.OpenWeb())
   {
       str = oSPWeb.Title;
       str = oSPWeb.Url;
   }
}  
Dim str As String

Using oSPsite As New SPSite("https://server")
  Using oSPWeb As SPWeb = oSPSite.OpenWeb()
     str = oSPWeb.Title
     str = oSPWeb.Url
  End Using
End Using

Taking advantage of using statements can greatly simplify your code. As noted in the C# Reference (using Statement), the common language runtime translates using statementsinto try and finally blocks, and any objects that implement the IDisposable interface are disposed for you. In many cases, however, using statements are not advisable, or must be used with some caution and understanding of what the runtime is doing. The following code example shows one case where you would not want the runtime to construct a finally block and dispose objects for you. In this case, SPContext returns an SPWeb object.

// Do not do this. Dispose() is automatically called on SPWeb. 
using( SPWeb web = SPControl.GetContextWeb(HttpContext.Current)) { ... }
' Do not do this. Dispose() is automatically called on SPWeb. 
Using web As SPWeb = SPControl.GetContextWeb(HttpContext.Current)
    '.......
End Using

SPContext objects are managed by the SharePoint framework and should not be explicitly disposed in your code. This is true also for the SPSite and SPWeb objects returned by SPContext.Site, SPContext.Current.Site, SPContext.Web, and SPContext.Current.Web.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_220.

You must be cautious and aware of what the runtime is doing whenever you combine SharePoint object model calls on the same line. Leaks arising from this scenario are among the hardest to find.

In the following code example, an SPSite object is instantiated but not disposed, because the runtime ensures disposal of only the SPWeb object returned by OpenWeb.

void CombiningCallsLeak()
{
    using (SPWeb web = new SPSite(SPContext.Current.Web.Url).OpenWeb())
    {
        // ... New SPSite will be leaked.
    } // SPWeb object web.Dispose() automatically called.
}
Private Sub CombiningCallsLeak()
    Using web As SPWeb = New SPSite(SPContext.Current.Web.Url).OpenWeb()
        ' ... New SPSite will be leaked.
    End Using ' SPWeb object web.Dispose() automatically called.
End Sub

You can fix this problem by nesting one using statement within another.

void CombiningCallsBestPractice()
{
    using (SPSite siteCollection = new SPSite(SPContext.Current.Web.Url))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
        // Perform operations on site.
        } // SPWeb object web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub CombiningCallsBestPractice()
    Using siteCollection As New SPSite(SPContext.Current.Web.Url)
        Using web As SPWeb = siteCollection.OpenWeb()
            ' Perform operations on site.
        End Using ' SPWeb object web.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

If you are not performing any operations on the SPSite object, you could write this more succinctly, as in the following code example.

void CombiningCallsBestPractice()
{
    using (SPSite siteCollection = new SPSite(SPContext.Current.Web.Url))
    using (SPWeb web = siteCollection.OpenWeb())
        {
        // Perform operations on site.
        } // SPWeb object web.Dispose() automatically called; SPSite object 
          // siteCollection.Dispose() automatically called.
}
Private Sub CombiningCallsBestPractice()
    Using siteCollection As New SPSite(SPContext.Current.Web.Url)
        Using web As SPWeb = siteCollection.OpenWeb()
            ' Perform operations on site.
        End Using ' SPWeb object web.Dispose() automatically called; SPSite object
    End Using
    ' siteCollection.Dispose() automatically called.
End Sub

In other cases, you must construct your own try, catch, and finally blocks. The most obvious examples are scenarios where you need to handle exceptions, and therefore must include a catch block. The following section provides guidelines on when and how to use try, catch, and finally blocks.

The try, catch, and finally Blocks

Using try, catch, and finally blocks obviously makes sense whenever you need to handle exceptions. Any code within a try/catch block should have a governing finally clause, which ensures that the objects that implement IDisposable are disposed. Notice that in the following code example you should fill the catch block with code that handles the exception. Never leave a catch block empty. Also note the best practice of testing for null before disposing.

String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;

try
{
   oSPSite = new SPSite("https://server");
   oSPWeb = oSPSite.OpenWeb(..);

   str = oSPWeb.Title;
}
catch(Exception e)
{
   // Handle exception, log exception, etc.
}
finally
{
   if (oSPWeb != null)
     oSPWeb.Dispose();

   if (oSPSite != null)
      oSPSite.Dispose();
}
Dim str As String
Dim oSPSite As SPSite = Nothing
Dim oSPWeb As SPWeb = Nothing

Try
   oSPSite = New SPSite("https://server")
   oSPWeb = oSPSite.OpenWeb(..)

   str = oSPWeb.Title
Catch e As Exception
' Handle exception, log exception, etc.
Finally
   If oSPWeb IsNot Nothing Then
 oSPWeb.Dispose()
   End If

   If oSPSite IsNot Nothing Then
  oSPSite.Dispose()
   End If
End Try

Try and finally blocks or a using statement would be required to avoid potential leaks when you create a disposable object within a foreach block, as shown in the following code example.

public static void SPSiteCollectionForEachBestPractice()
{
     string sUrl = "http://spvm";
 
      using (SPSite siteCollectionOuter = new SPSite(sUrl))
     {
         SPWebApplication webApp = siteCollectionOuter.WebApplication;
         SPSiteCollection siteCollections = webApp.Sites;

                  SPSite siteCollectionInner = null;
                  foreach (siteCollectionInner in siteCollections)
             {
                      try  // Should be first statement after foreach.
                      {
                          Console.WriteLine(siteCollectionInner.Url);
                          // Exception occurs here.
                      }
                      finally
                      {
                          if(siteCollectionInner != null)
                          siteCollectionInner.Dispose();
                      }
             }
     } // SPSite object siteCollectionOuter.Dispose() automatically called.
 }
Public Shared Sub SPSiteCollectionForEachBestPractice()
    Dim sUrl As String = "http://spvm"

    Using siteCollectionOuter As New SPSite(sUrl)
        Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
        Dim siteCollections As SPSiteCollection = webApp.Sites

        Dim siteCollectionInner As SPSite = Nothing
        For Each siteCollectionInner In siteCollections
            Try ' Should be first statement after foreach.
                Console.WriteLine(siteCollectionInner.Url)
                ' Exception occurs here.
            Finally
                If siteCollectionInner IsNot Nothing Then
                    siteCollectionInner.Dispose()
                End If
            End Try
        Next
    End Using
End Sub ' SPSite object siteCollectionOuter.Dispose() automatically called.

Response.Redirect with try, catch, and finally Blocks and using Statements

The finally block executes after calls to Response.Redirect in the try block. Response.Redirect ultimately generates a ThreadAbortException exception. When this exception is raised, the runtime executes all finally blocks before ending the thread. However, because the finally block can do an unbounded computation or cancel the ThreadAbortException, the thread will not necessarily end. Therefore, before any redirection or transfer of processing can occur, you must dispose the objects. If your code must redirect, implement it in a way similar to the following code example.

String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;

try
{
   oSPSite = new SPSite("https://server");
   oSPWeb = oSPSite.OpenWeb(..);

   str = oSPWeb.Title;
   if(bDoRedirection)
   {
       if (oSPWeb != null)
          oSPWeb.Dispose();
    
       if (oSPSite != null)
          oSPSite.Dispose();

       Response.Redirect("newpage.aspx");
   }
}
catch(Exception e)
{
}
finally
{
   if (oSPWeb != null)
     oSPWeb.Dispose();

   if (oSPSite != null)
      oSPSite.Dispose();
}
Dim str As String
Dim oSPSite As SPSite = Nothing
Dim oSPWeb As SPWeb = Nothing

Try
   oSPSite = New SPSite("https://server")
   oSPWeb = oSPSite.OpenWeb(..)

   str = oSPWeb.Title
   If bDoRedirection Then
         If oSPWeb IsNot Nothing Then
                 oSPWeb.Dispose()
         End If
         If oSPSite IsNot Nothing Then
              oSPSite.Dispose()
         End If

         Response.Redirect("newpage.aspx")
   End If
Catch e As Exception
Finally
   If oSPWeb IsNot Nothing Then
         oSPWeb.Dispose()
   End If

   If oSPSite IsNot Nothing Then
         oSPSite.Dispose()
   End If
End Try

Because a using statement instructs the runtime to create a finally block, whenever you use Response.Redirect within a using statement, ensure that objects are disposed properly. The following code example shows how you can do this.

using (SPSite oSPSite = new SPSite("https://server"))
using (SPWeb oSPWeb = oSPSite.OpenWeb(..))
{
    if (bDoRedirection)
        Response.Redirect("newpage.aspx");
}
Using oSPSite As New SPSite("https://server")
    Using oSPWeb As SPWeb = oSPSite.OpenWeb(..)
        If bDoRedirection Then
            Response.Redirect("newpage.aspx")
        End If
    End Using
End Using

Recommendations to Reduce Long-Term Object Retention

You can reduce long-term retention of SharePoint objects by following these general recommendations.

  • If you create the object with a new operator, ensure that the creating application disposes of it.

    Note

    This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_110.

    Good Coding Practice #1

    Explicitly disposing

    void CreatingSPSiteExplicitDisposeNoLeak()
    {
        SPSite siteCollection = null;
        try
        {
            siteCollection = new SPSite("http://moss");
        }
        finally
        {
            if (siteCollection != null)
                siteCollection.Dispose();
        }
    }
    
    Private Sub CreatingSPSiteExplicitDisposeNoLeak()
        Dim siteCollection As SPSite = Nothing
        Try
            siteCollection = New SPSite("http://moss")
        Finally
            If siteCollection IsNot Nothing Then
                siteCollection.Dispose()
            End If
        End Try
    End Sub
    

    Good Coding Practice #2

    Automatically disposing

    CreatingSPSiteWithAutomaticDisposeNoLeak()
    {
        using (SPSite siteCollection = new SPSite("http://moss"))
        {
        } // SPSite object siteCollection.Dispose() is called automatically.
    }
    
    CreatingSPSiteWithAutomaticDisposeNoLeak()
    {
        using (SPSite siteCollection = new SPSite("http://moss"))
        {
        } // SPSite object siteCollection.Dispose() is called automatically.
    }
    
  • Dispose items created by SharePoint methods that return other SPWeb objects (such as OpenWeb()).

    Note

    This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_120.

    Good Coding Practice

    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.
    }
    
    Private Sub OpenWebNoLeak()
            Using siteCollection As New SPSite("http://moss")
                    Using web As SPWeb = siteCollection.OpenWeb()
                    End Using ' SPWeb object web.Dispose() automatically called.
            End Using ' SPSite object siteCollection.Dispose() automatically called.
    End Sub
    
  • Do not share any SPRequest object (and by extension any object that contains a reference to an SPRequest object) across threads. Any coding technique that shares an SPRequest object between two or more threads, or creates an SPRequest object on one thread and disposes it on another, is not supported. This means that you cannot store any object that holds a reference to an SPRequest object in a static variable. Do not, therefore, store SharePoint objects that implement IDisposable (such as SPWeb or SPSite) in static variables.

SPSite Objects

This section describes situations in which new SPSite objects are returned and must be disposed.

In general, any time a calling application uses the new SPSite constructors (any signature), it should call the Dispose() method when it is finished using the object. If the SPSite object is obtained from GetContextSite(), the calling application should not dispose the object. Because the SPWeb and SPSite objects keep an internal list that is derived in this way, disposing of the object may cause the SharePoint object model to behave unpredictably. Internally, SharePoint Foundation enumerates over this list after page completion to dispose the objects properly.

SPSiteCollection Class

This section describes the methods, properties, or operators in the SPSiteCollection object that require the returned SPSite object to be closed after access.

SPSiteCollection.Add Method

The SPSiteCollection.Add method creates and returns a new SPSite object. You should dispose any SPSite object returned from the SPSiteCollection.Add method.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_240.

Bad Coding Practice

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.
}
Private Sub SPSiteCollectionAddLeak()
        Dim webApp As SPWebApplication = New SPSite("http://moss").WebApplication
        Dim siteCollections As SPSiteCollection = webApp.Sites
        Dim siteCollection As SPSite = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\User", "roger.lamb@litwareinc.com")
        ' SPSite siteCollection leak.
End Sub

Good Coding Practice

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.
}
Private Sub SPSiteCollectionAddNoLeak()
      Dim webApp As SPWebApplication = New SPSite("http://moss").WebApplication
      Dim siteCollections As SPSiteCollection = webApp.Sites
      Using siteCollection As SPSite = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\User", "roger.lamb@litwareinc.com")
      End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

SPSiteCollection [ ] Index Operator

The SPSiteCollection [] index operator returns a new SPSite object for each access. An SPSite instance is created even if that object was already accessed. The following code examples demonstrate improper disposal of the SPSite object.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_230.

Bad Coding Practice #1

Using index operator

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.
}
Private Sub SPSiteCollectionIndexerLeak()
    Using siteCollectionOuter As New SPSite("http://moss")
        Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
        Dim siteCollections As SPSiteCollection = webApp.Sites

        Dim siteCollectionInner As SPSite = siteCollections(0)
        ' SPSite siteCollectionInner leak. 
    End Using ' SPSite object siteCollectionOuter.Dispose() automatically called.
End Sub

Bad Coding Practice #2

Using foreach loop

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.
}
Private Sub SPSiteCollectionForEachLeak()
    Using siteCollectionOuter As New SPSite("http://moss")
        Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
        Dim siteCollections As SPSiteCollection = webApp.Sites

        For Each siteCollectionInner As SPSite In siteCollections
            ' SPSite siteCollectionInner leak.
        Next siteCollectionInner
    End Using ' SPSite object siteCollectionOuter.Dispose() automatically called.
End Sub

Good Coding Practice #1

Using index operator

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.
}
Private Sub SPSiteCollectionIndexerNoLeak()
    Using siteCollectionOuter As New SPSite("http://moss")
        Dim siteCollectionInner As SPSite = Nothing
        Try
            Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
            Dim siteCollections As SPSiteCollection = webApp.Sites

            siteCollectionInner = siteCollections(0)
        Finally
            If siteCollectionInner IsNot Nothing Then
                siteCollectionInner.Dispose()
            End If
        End Try
    End Using ' SPSite object siteCollectionOuter.Dispose() automatically called.
End Sub

Good Coding Practice #2

Using foreach loop

void SPSiteCollectionForEachNoLeak()
{
    using (SPSite siteCollectionOuter = new SPSite("http://yoursite”))
    {
        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.
}
Private Sub SPSiteCollectionForEachNoLeak()

    Using siteCollectionOuter As SPSite = New SPSite("http://yoursite")
        Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
        Dim siteCollections As SPSiteCollection = webApp.Sites
        For Each siteCollectionInner As SPSite In siteCollections
            Try
                ' ...
            Finally
                If siteCollectionInner IsNot Nothing Then
                    siteCollectionInner.Dispose()
                End If
            End Try
        Next
    End Using
End Sub

SPSite.AllWebs Property (SPWebCollection)

This section describes the methods, properties, or operators in the AllWebs property collection that require the SPWeb object to be closed after access.

SPSite.AllWebs.Add Method

The SPSite.AllWebs.Add method creates and returns an SPWeb object. You should dispose any SPWeb object returned from SPSite.AllWebs.Add.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_150.

Bad Coding Practice

void AllWebsAddLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        SPWeb web = siteCollection.AllWebs.Add("site-relative URL");
        // SPWeb object leaked.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}
Private Sub AllWebsAddLeak()
    Using siteCollection As New SPSite("http://moss")
        Dim web As SPWeb = siteCollection.AllWebs.Add("site-relative URL")
        ' SPWeb object leaked.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

Good Coding Practice

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. 
}
Private Sub AllWebsAddNoLeak()
    Using siteCollection As New SPSite("http://moss")
        Using web As SPWeb = siteCollection.AllWebs.Add("site-relative URL")
        End Using ' SPWeb object web.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

SPWebCollection.Add Method

The SPWebCollection.Add method creates and returns an SPWeb object that needs to be disposed.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_200.

Bad Coding Practice

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 innerWeb.
            // innerWeb leak.
        } // SPWeb object outerWeb.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}
Private Sub SPWebCollectionAddLeak(ByVal strWebUrl As String)
    Using siteCollection As New SPSite("http://moss")
        Using outerWeb As SPWeb = siteCollection.OpenWeb()
            Dim webCollection As SPWebCollection = siteCollection.AllWebs ' No AllWebs leak just getting reference.
            Dim innerWeb As SPWeb = webCollection.Add(strWebUrl) ' Must dispose innerWeb.
            ' innerWeb leak.
        End Using ' SPWeb object outerWeb.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

Good Coding Practice

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. 
}
Private Sub SPWebCollectionAddNoLeak(ByVal strWebUrl As String)
    Using siteCollection As New SPSite("http://moss")
        Using outerWeb As SPWeb = siteCollection.OpenWeb()
            Dim webCollection As SPWebCollection = siteCollection.AllWebs ' No AllWebs leak just getting reference.
            Using innerWeb As SPWeb = webCollection.Add(strWebUrl)
                '...
            End Using
        End Using ' SPWeb object outerWeb.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

SPSite.AllWebs [ ] Index Operator

The SPSite.AllWebs [] index operator returns a new SPWeb instance each time it is accessed. An object is created during the indexing operation even if that object was already accessed. If not properly closed, the following code samples leave an SPWeb object in the .NET Framework garbage collector.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_130.

Bad Coding Practice

void AllWebsForEachLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            foreach (SPWeb innerWeb in siteCollection.AllWebs)
            {
                // Explicitly dispose here to avoid out of memory leaks with large number of SPWeb objects.
            }
        } // SPWeb object outerWeb.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}
Private Sub AllWebsForEachLeak()
    Using siteCollection As New SPSite("http://moss")
        Using outerWeb As SPWeb = siteCollection.OpenWeb()
            For Each innerWeb As SPWeb In siteCollection.AllWebs
                ' Explicitly dispose here to avoid out of memory leaks with large number of SPWeb objects.
            Next
        End Using ' SPWeb object outerWeb.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

Good Coding Practice #1

Using foreach loop

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. 
}
Private Sub AllWebsForEachNoLeakOrMemoryOOM()
    Using siteCollection As New SPSite("http://moss")
        Using outerWeb As SPWeb = siteCollection.OpenWeb()
            For Each innerWeb As SPWeb In siteCollection.AllWebs
                Try
                    ' ...
                Finally
                    If innerWeb IsNot Nothing Then
                        innerWeb.Dispose()
                    End If
                End Try
            Next
        End Using ' SPWeb object outerWeb.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

Good Coding Practice #2

Using index operator

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. 
}
Private Sub AllWebsIndexerNoLeak()
    Using siteCollection As New SPSite("http://moss")
        Using web As SPWeb = siteCollection.AllWebs(0)
        End Using ' SPWeb object web.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

SPSite.OpenWeb and SPSite. SelfServiceCreateSite Methods

The OpenWeb() method and the SelfServiceCreateSite method (all signatures) of the SPSite object create an SPWeb object and return it to the caller. This new object is not stored in the SPSite object and is not disposed anywhere in the SPSite class. For this reason, you should dispose any object created via these methods.

Bad Coding Practice

void OpenWebLeak()
{
    using (SPWeb web = new SPSite(SPContext.Current.Web.Url).OpenWeb())
    {
        // SPSite leaked !
    } // SPWeb object web.Dispose() automatically called.
}
Private Sub OpenWebLeak()
    Using web As SPWeb = New SPSite(SPContext.Current.Web.Url).OpenWeb()
        ' SPSite leaked !
    End Using ' SPWeb object web.Dispose() automatically called.
End Sub

Good Coding Practice

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.
}
Private Sub OpenWebNoLeak()
    Using siteCollection As New SPSite("http://moss")
        Using web As SPWeb = siteCollection.OpenWeb()
        End Using ' SPWeb object web.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

SPSite.RootWeb Property

Previous guidance indicated that the calling application should dispose of the SPSite.RootWeb property just before disposing of the SPSite object that is using it. This is no longer the official guidance. The dispose cleanup is handled automatically by both SharePoint Foundation and SharePoint Server. Additionally, SPSite properties LockIssue, Owner, and SecondaryContact used the RootWeb property internally. Given the updated guidance for RootWeb, it is no longer advisable to call the Dispose method on the SPSite.RootWeb property whenever any of these properties are used.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_140.

Good Coding Practice

public void RootWebBestPractice()
{
    // New SPSite.
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        SPWeb rootWeb1 = siteCollection.RootWeb;
        // No explicit rootWeb1 dispose required.
    }  // siteCollection automatically disposed by implementing using().
    // rootWeb1 will be Disposed by SPSite.

    // SPContext and SPControl
    SPWeb rootWeb2 = SPContext.Current.Site.RootWeb;
    // Also would apply to SPControl.GetContextSite(Context);
    // No explicit rootWeb2 dispose required because it is obtained from SPContext.Current.Site.
}
Public Sub RootWebBestPractice()
    ' New SPSite.
    Using siteCollection As New SPSite("http://moss")
        Dim rootWeb1 As SPWeb = siteCollection.RootWeb
        ' No explicit rootWeb1 dispose required.
    End Using ' siteCollection automatically disposed by implementing using().
    ' rootWeb1 will be Disposed by SPSite.

    ' SPContext and SPControl
    Dim rootWeb2 As SPWeb = SPContext.Current.Site.RootWeb
    ' Also would apply to SPControl.GetContextSite(Context);
    ' No explicit rootWeb2 dispose required because it is obtained from SPContext.Current.Site.
End Sub

Microsoft.Office.Server.UserProfiles.PersonalSite (Office SharePoint Server 2007 only)

The Microsoft.Office.Server.UserProfiles.PersonalSite returns an SPSite object that must be disposed.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_400.

Bad Coding Practice

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.
    }
}
Private Sub PersonalSiteLeak()
    ' Open a site collection.
    Using siteCollection As New SPSite("http://moss")
        Dim profileManager As New UserProfileManager(ServerContext.GetContext(siteCollection))
        Dim profile As UserProfile = profileManager.GetUserProfile("domain\username")
        Dim personalSite As SPSite = profile.PersonalSite ' Will leak.
    End Using
End Sub

Good Coding Practice

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)
        {
            // ...
        }
    }
}
Private Sub PersonalSiteNoLeak()
    ' Open a site collection.
    Using siteCollection As New SPSite("http://moss")
        Dim profileManager As New UserProfileManager(ServerContext.GetContext(siteCollection))
        Dim profile As UserProfile = profileManager.GetUserProfile("domain\username")
        Using personalSite As SPSite = profile.PersonalSite
            ' ...
        End Using
    End Using
End Sub

In another edge case, UserProfiles.PersonalSite leaks, as shown in the following code example.

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.
    }
}
Private Sub PersonalSiteLeak()
    ' Open a site collection.
    Using siteCollection As New SPSite("http://moss")
        Dim profileManager As New UserProfileManager(ServerContext.GetContext(siteCollection))
        Dim profile As UserProfile = profileManager.GetUserProfile("domain\username")
        Dim personalSite As SPSite = profile.PersonalSite ' Will leak.
    End Using
End Sub

You can resolve this sort of leak by following the pattern shown in the following code example.

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)
        {
            // ...
        }
    }
}
Private Sub PersonalSiteNoLeak()
    ' Open a site collection
    Using siteCollection As New SPSite("http://moss")
        Dim profileManager As New UserProfileManager(ServerContext.GetContext(siteCollection))
        Dim profile As UserProfile = profileManager.GetUserProfile("domain\username")
        Using personalSite As SPSite = profile.PersonalSite
            ' ...
        End Using
    End Using
End Sub

Also notice that you can improve performance (and avoid creating an SPSite object) by retrieving a PersonalSite object from the ProfileLoader, as shown in the following code example.

UserProfile myProfile = ProfileLoader.GetProfileLoader().GetUserProfile();
using (SPSite personalSite = myProfile.PersonalSite)
{
     // ...
}
Dim myProfile As UserProfile = ProfileLoader.GetProfileLoader().GetUserProfile()
Using personalSite As SPSite = myProfile.PersonalSite
       ' ...
End Using

Additionally, if you are creating a Web Part for a My Site, you can use an instance of PersonalSite that does not need to be disposed.

IPersonalPage currentMySitePage = this.Page as IPersonalPage;
if (currentMySitePage != null && !currentMySitePage.IsProfileError)
{
     SPSite personalSite = currentMySitePage.PersonalSite; // Will not leak.
     // ...
}
Dim currentMySitePage As IPersonalPage = TryCast(Me.Page, IPersonalPage)
If currentMySitePage IsNot Nothing AndAlso (Not currentMySitePage.IsProfileError) Then
         Dim personalSite As SPSite = currentMySitePage.PersonalSite ' Will not leak.
         ' ...
End If

SPWeb Objects

This section describes the situations in which SPWeb objects are returned and may need to be disposed.

SPWeb.ParentWeb Property

Updated Guidance

Previous guidance recommended that the calling application should dispose of the SPWeb.ParentWeb. This is no longer the official guidance. The dispose cleanup is handled automatically by both SharePoint Foundation and SharePoint Server.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_170.

Good Coding Practice

using (SPSite site = new SPSite("https://localhost")) 
{
    using (SPWeb web = site.OpenWeb())
    {
        SPList list = web.Lists["Announcements"];
        SPWeb parentWeb = list.ParentWeb; //No explicit dispose required.
    }
}
Using site As New SPSite("https://localhost")
      Using web As SPWeb = site.OpenWeb()
            Dim list As SPList = web.Lists("Announcements")
            Dim parentWeb As SPWeb = list.ParentWeb 'No explicit dispose required.
      End Using
End Using

SPWeb.Webs Property

This section describes the methods, properties, or operators in the Webs property collection that require disposal of the SPWeb object after access.

SPWeb.Webs

The SPWeb.Webs property returns an SPWebCollection object. The SPWeb objects in this collection must be disposed.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_180.

Bad Coding Practice

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. 
}
Private Sub WebsLeak()
    Using siteCollection As New SPSite("http://moss")
        Using outerWeb As SPWeb = siteCollection.OpenWeb()
            For Each innerWeb As SPWeb In outerWeb.Webs
                ' SPWeb innerWeb leak.
            Next
        End Using ' SPWeb object outerWeb.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

Good Coding Practice

void WebsNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb outerWeb = siteCollection.OpenWeb())
        {
            foreach (SPWeb innerWeb in outerWeb.Webs)
            {
                try // Should be first statement after foreach.
                {
                    // ...
                }
                finally
                {
                    if(innerWeb != null)
                        innerWeb.Dispose();
                }
            }
        } // SPWeb object outerWeb.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}
Private Sub WebsNoLeak()
    Using siteCollection As New SPSite("http://moss")
        Using outerWeb As SPWeb = siteCollection.OpenWeb()
            For Each innerWeb As SPWeb In outerWeb.Webs
                Try ' Should be first statement after foreach.
                    ' ...
                Finally
                    If innerWeb IsNot Nothing Then
                        innerWeb.Dispose()
                    End If
                End Try
            Next
        End Using ' SPWeb object outerWeb.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

SPWeb.Webs.Add

The SPWeb.Webs.Add method (or SPWebCollection.Add) creates and returns a new SPWeb object. You should dispose of any SPWeb object returned from this method call.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_190.

Bad Coding Practice

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.
}
Private Sub WebsAddLeak(ByVal strWebUrl As String)
    Using siteCollection As New SPSite("http://moss")
        Using web As SPWeb = siteCollection.OpenWeb()
            Dim addedWeb As SPWeb = web.Webs.Add(strWebUrl) ' Will leak.

        End Using ' SPWeb object web.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

Good Coding Practice

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.
}
Private Sub WebsAddNoLeak(ByVal strWebUrl As String)
    Using siteCollection As New SPSite("http://moss")
        Using web As SPWeb = siteCollection.OpenWeb()
            Using addedWeb As SPWeb = web.Webs.Add(strWebUrl)
                '..
            End Using

        End Using ' SPWeb object web.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

SPWeb.Webs[] Index Operator

The SPWeb.Webs[] index operator returns a new SPWeb object for each access. An SPWeb is created by calling the OpenWeb method, even if that object was already accessed. The following code samples cause long-term retention of these objects in memory used by the .NET Framework.

Bad Coding Practice #1

Using For loop

int i;

SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);

oSPWeb = oSPSite.OpenWeb();

for(i = 0;i < oSPWeb.Webs.Count;i++)
{
   oSPWeb2 = oSPWeb.Webs[i];
   BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
}
Dim i As Integer

Dim oSPWeb, oSPWeb2 As SPWeb
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)

oSPWeb = oSPSite.OpenWeb()

For i = 0 To oSPWeb.Webs.Count - 1
   oSPWeb2 = oSPWeb.Webs(i)
   BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
Next i

Bad Coding Practice #2

Using foreach loop

SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);

oSPWeb = oSPSite.OpenWeb();

foreach(SPWeb oSPWeb2 in oSPWebe.Webs)
{
   BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
}
Dim oSPWeb, oSPWeb2 As SPWeb
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)

oSPWeb = oSPSite.OpenWeb()

For Each oSPWeb2 As SPWeb In oSPWebe.Webs
   BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
Next

The recommended fix is to dispose at the end of each loop.

Good Coding Practice #1

Using For loop

int i;

SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);

oSPWeb = oSPSite.OpenWeb();

for(i = 0;i < oSPWeb.Webs.Count;i++)
{
   oSPWeb2 = oSPWeb.Webs[i];
   BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
   oSPWeb2.Dispose();
}

oSPWeb.Dispose();
Dim i As Integer

Dim oSPWeb, oSPWeb2 As SPWeb
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)

oSPWeb = oSPSite.OpenWeb()

For i = 0 To oSPWeb.Webs.Count - 1
   oSPWeb2 = oSPWeb.Webs(i)
   BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
   oSPWeb2.Dispose()
Next i

oSPWeb.Dispose()

Good Coding Practice #2

Using foreach loop

SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);

oSPWeb = oSPSite.OpenWeb();

foreach(SPWeb oSPWeb2 in oSPWeb.Webs)
{
   BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
   oSPWeb2.Dispose();
}

oSPWeb.Dispose();
Dim oSPWeb, oSPWeb2 As SPWeb
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)

oSPWeb = oSPSite.OpenWeb()

For Each oSPWeb2 As SPWeb In oSPWeb.Webs
   BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
   oSPWeb2.Dispose()
Next

oSPWeb.Dispose()

Good Coding Practice #3

Using for loop with automatic disposal

int i;

SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);

using(oSPWeb = oSPSite.OpenWeb())
{
   for(i = 0;i < oSPWeb.Webs.Count;i++)
   {
      Using(oSPWeb2 = oSPWeb.Webs[i])
      {
         BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
      }
   }
}
Dim i As Integer
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)

Using oSPWeb As SPWeb = oSPSite.OpenWeb()
    For i = 0 To oSPWeb.Webs.Count - 1
        Using oSPWeb2 As SPWeb = oSPWeb.Webs(i)
                BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
        End Using
    Next
End Using

Other Objects That Require Disposal

This section describes when to call the Dispose method on other SharePoint objects.

Microsoft.SharePoint.Portal.SiteData.Area.Web Property

The Web property of the SharePoint.Portal.SiteData.Area class returns a new SPWeb object each time it is accessed. Any use of the Area.Web property should have a corresponding call to the Dispose method. Although the Area and AreaManager classes are now obsolete, this remains a concern when migrating legacy code.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_500.

Bad Coding Practice

void AreaWebLeak()
{
    // AreaManager and Area are obsolete in SharePoint Server, but this
    // should still be noted.
    Area area = AreaManager.GetArea(PortalContext.Current, new Guid("{GUID}"));
    string str = area.Web.Title;
    // SPWeb area.Web leak.
}
Private Sub AreaWebLeak()
    ' AreaManager and Area are obsolete in SharePoint Server, but this
    ' should still be noted.
    Dim area As Area = AreaManager.GetArea(PortalContext.Current, New Guid("{GUID}"))
    Dim str As String = area.Web.Title
    ' SPWeb area.Web leak.
End Sub

Good Coding Practice

public void AreaWebNoLeak()
{
    // AreaManager and Area are obsolete but this should still be noted.
    Area area = AreaManager.GetArea(PortalContext.Current, new Guid("{GUID}"));
    using (SPWeb areaWeb = area.Web)
    {
        string str = areaWeb.Title;
    }
}
Public Sub AreaWebNoLeak()
    ' AreaManager and Area are obsolete but this should still be noted.
    Dim area As Area = AreaManager.GetArea(PortalContext.Current, New Guid("{GUID}"))
    Using areaWeb As SPWeb = area.Web
        Dim str As String = areaWeb.Title
    End Using
End Sub

SPControl.GetContextSite and SPControl.GetContextWeb Methods

If the object is obtained from the SharePoint context objects (the GetContextSite and GetContextWeb methods in the SPControl class), the calling application should not call the Dispose method on the object. Doing so may cause the SharePoint object model to behave unpredictably or fail. This is due to an internal list that is kept in the SPSite and SPWeb objects derived in this way. Internally, the object model enumerates over this list after page completion to dispose the objects properly.

You should still dispose an object that is created from these objects, for example, if a Web site is opened from an SPSite object that you obtained by using the GetContextSite method.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_210.

Bad Coding Practice

void SPControlBADPractice()
{
    SPSite siteCollection = SPControl.GetContextSite(Context);
    siteCollection.Dispose();   // DO NOT DO THIS.
    SPWeb web = SPControl.GetContextWeb(Context);
    web.Dispose();  // DO NOT DO THIS.
}
Private Sub SPControlBADPractice()
    Dim siteCollection As SPSite = SPControl.GetContextSite(Context)
    siteCollection.Dispose() ' DO NOT DO THIS.
    Dim web As SPWeb = SPControl.GetContextWeb(Context)
    web.Dispose() ' DO NOT DO THIS.
End Sub

Good Coding Practice

void SPControlBestPractice()
{
    SPSite siteCollection = SPControl.GetContextSite(Context);
    SPWeb web = SPControl.GetContextWeb(Context);
    // Do NOT call Dispose().
}
Private Sub SPControlBestPractice()
      Dim siteCollection As SPSite = SPControl.GetContextSite(Context)
      Dim web As SPWeb = SPControl.GetContextWeb(Context)
      ' Do NOT call Dispose().
End Sub

Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager

The SPLimitedWebPartManager class contains a reference to an internal SPWeb object that must be disposed.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_160.

Bad Coding Practice

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. 
}
Private Sub SPLimitedWebPartManagerLeak()
    Using siteCollection As New SPSite("http://moss")
        Using web As SPWeb = siteCollection.OpenWeb()
            Dim page As SPFile = web.GetFile("Source_Folder_Name/Source_Page")
            Dim webPartManager As SPLimitedWebPartManager = page.GetLimitedWebPartManager(PersonalizationScope.Shared)
            ' SPWeb object webPartManager.Web leaked.
        End Using ' SPWeb object web.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

Good Coding Practice

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);
                webPartManaber.Web.Dispose();
        } // SPWeb object web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}
Private Sub SPLimitedWebPartManagerLeak()
    Using siteCollection As New SPSite("http://moss")
        Using web As SPWeb = siteCollection.OpenWeb()
            Dim page As SPFile = web.GetFile("Source_Folder_Name/Source_Page")
            Dim webPartManager As SPLimitedWebPartManager = page.GetLimitedWebPartManager(PersonalizationScope.Shared)
            webPartManaber.Web.Dispose()
        End Using ' SPWeb object web.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

Microsoft.SharePoint.Publishing.PublishingWeb

Note

The Microsoft.SharePoint.Publishing namespace belongs to SharePoint Server 2010. This section applies to SharePoint Server 2010 and not to SharePoint Foundation 2010.

The GetPublishingWebs method of the PublishingWeb class returns a PublishingWebCollection object. You must call the Close method on each enumerated innerPubWeb object. When you are calling only the GetPublishingWeb method, you are not required to call Close.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_300.

Bad Coding Practice

void PublishingWebCollectionLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            // Passing in SPWeb object that 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.
}
Private Sub PublishingWebCollectionLeak()
    Using siteCollection As New SPSite("http://moss")
        Using web As SPWeb = siteCollection.OpenWeb()
            ' Passing in SPWeb object that you own, no dispose needed on
            ' outerPubWeb.
            Dim outerPubWeb As PublishingWeb = PublishingWeb.GetPublishingWeb(web)

            Dim pubWebCollection As PublishingWebCollection = outerPubWeb.GetPublishingWebs()
            For Each innerPubWeb As PublishingWeb In pubWebCollection
                ' innerPubWeb leak.
            Next
            ' PublishingWeb will leak for each innerPubWeb referenced
        End Using ' SPWeb object web.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

Good Coding Practice

void PublishingWebCollectionNoLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            // Passing in SPWeb object that 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();
                }
            }
        }  // SPWeb object web.Dispose() automatically called.
    } // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub PublishingWebCollectionNoLeak()
    Using siteCollection As New SPSite("http://moss")
        Using web As SPWeb = siteCollection.OpenWeb()
            ' Passing in SPWeb object that you own, no dispose needed on
            ' outerPubWeb.
            Dim outerPubWeb As PublishingWeb = PublishingWeb.GetPublishingWeb(web)
            Dim pubWebCollection As PublishingWebCollection = outerPubWeb.GetPublishingWebs()
            For Each innerPubWeb As PublishingWeb In pubWebCollection
                Try
                    ' ...
                Finally
                    If innerPubWeb IsNot Nothing Then
                        innerPubWeb.Close()
                    End If
                End Try
            Next
        End Using ' SPWeb object web.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

Note

There is a similar requirement to call Close on each PublishingWeb object created by calling the Add method on the PublishingWebCollection returned by Microsoft.SharePoint.Publishing.PublishingWeb.GetPublishingWebs. For a code example, see the GetPublishingWebs() method. This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_310.

The Microsoft.SharePoint.Publishing.PublishingWeb.GetVariation method returns a PublishingWeb object that must be disposed.

Note

This best practice addresses the issue identified by the SharePoint Dispose Checker Tool as SPDisposeCheckID_320.

Bad Coding Practice

void GetVariationLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);  // Passing in SPWeb object, so no Close() needed
            VariationLabel variationLabel = Variations.Current.UserAccessibleLabels[0];
            PublishingWeb variationPublishingWeb = publishingWeb.GetVariation(variationLabel);  // Must be Closed().
            // ...
        } // SPWeb object outerWeb.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}
Private Sub GetVariationLeak()
    Using siteCollection As New SPSite("http://moss")
        Using web As SPWeb = siteCollection.OpenWeb()
            Dim publishingWeb As PublishingWeb = PublishingWeb.GetPublishingWeb(web) ' Passing in SPWeb object, so no Close() needed
            Dim variationLabel As VariationLabel = Variations.Current.UserAccessibleLabels(0)
            Dim variationPublishingWeb As PublishingWeb = publishingWeb.GetVariation(variationLabel) ' Must be Closed().
            ' ...
        End Using ' SPWeb object outerWeb.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

Good Coding Practice

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 SPWeb object, 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 web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called. 
}
Private Sub GetVariationNoLeak()
    Using siteCollection As New SPSite("http://moss")
        Using web As SPWeb = siteCollection.OpenWeb()
            Dim variationPublishingWeb As PublishingWeb = Nothing
            Try
                Dim publishingWeb As PublishingWeb = PublishingWeb.GetPublishingWeb(web) ' Passing in SPWeb object, so no Close() needed.
                Dim variationLabel As VariationLabel = Variations.Current.UserAccessibleLabels(0)
                variationPublishingWeb = publishingWeb.GetVariation(variationLabel) ' Must be Closed().
                ' ...
            Finally
                If variationPublishingWeb IsNot Nothing Then
                    variationPublishingWeb.Close()
                End If
            End Try
        End Using ' SPWeb object web.Dispose() automatically called.
    End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub

Cross Method Dispose Patterns

The following example demonstrates the common practice of holding onto the SPSite and SPWeb objects across methods in a class. Sometimes this design pattern is required, but ensure that you do not overlook the appropriate time to call Dispose when you are finished with the cross method calls. The following code example shows a pattern where SPSite and SPWeb leak when the class goes out of scope.

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;
        }
    }
}
Public Class CrossMethodLeak
    Private _siteCollection As SPSite = Nothing
    Private _web As SPWeb = Nothing

    Public Sub MethodA()
        _siteCollection = New SPSite("http://moss")
        _web = _siteCollection.OpenWeb()
    End Sub

    Public Sub MethodB()
        If _web IsNot Nothing Then
            Dim title As String = _web.Title
        End If
    End Sub

    Public Sub MethodC()
        If _web IsNot Nothing Then
            Dim name As String = _web.Name
        End If
    End Sub
End Class

Conclusion

Because many SharePoint objects implement the IDisposable interface, you must take care when using these objects to avoid retaining them in memory. By following the guidelines for disposing of SharePoint objects, as described in this article, you can help to ensure reliability of your custom code.

Acknowledgements

We would like to thank the following people for their input and guidance in creating this article:

  • Steve Sheppard, Microsoft Corporation

  • Chris Gideon, Microsoft Corporation

  • Rashid Aga, Microsoft Corporation

See Also

Other Resources

SharePoint Dispose Checker Tool

Patterns & Practices SharePoint Guidance

Windows SharePoint Services Developer Center

SharePoint Server 2007 Developer Center

SharePoint Products and Technologies Customization Best Practices

Best Practices Resource Center for SharePoint Server 2007

Roger Lamb's SharePoint Developer Blog

Best Practices: Common Coding Issues When Using the SharePoint Object Model