http://msdn.microsoft.com/en-us/library/aa973248.aspx#sharepointobjmodel__spsiteobjects
The objects in the Windows SharePoint Services 3.0 object model
serve as an interface for working with Windows SharePoint Services data.
Frequently, developers call into the object model to read data from or
write new data to the Windows SharePoint Services store.
The Windows SharePoint Services object model contains 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 of 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 Windows SharePoint Services application pool, especially during peak usage
-
Application crashes that appear as heap corruption in the debugger
-
High memory use for Microsoft 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 Windows SharePoint Services objects, primarily the
SPSite class and
SPWeb
class objects, are created as managed objects. However, these objects
use unmanaged code and memory to perform the majority of their work. The
managed part of the object is much smaller than the unmanaged part.
Because the smaller managed part does not put memory pressure on the
garbage collector, the garbage collector does not release the object
from memory in a timely manner. The object's use of a large amount of
unmanaged memory can cause some of the unusual behaviors described
earlier. Calling applications that work with
IDisposable
objects in Windows SharePoint Services must dispose of the objects when
the applications finish using them. You should not rely on the garbage
collector to release them from memory automatically.
Finding Incorrectly Disposed Objects
You can identify the potential presence of incorrectly disposed objects by asking the following questions:
-
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 MB–1.5 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, you experience 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.
-
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.
-
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.
-
Does your system use custom or third-party Web Parts or custom applications?
You might not be aware that they must dispose of 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\12\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. Windows SharePoint
Services 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 garbage collected.
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 of 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 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 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 of 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 of 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 of 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:
-
Dispose method
-
using clause
-
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 Clause
You can automatically dispose of SharePoint objects that implement the
IDisposable interface by using the Microsoft Visual C#
using statement.
The following code provides an example.
String str;
using(SPSite oSPsite = new SPSite("http://server"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
str = oSPWeb.Title;
str = oSPWeb.Url;
}
}
Taking advantage of
using statements can greatly simplify your code. As noted in the C# Reference (
using Statement), the common language runtime translates
using clauses into
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)) { ... }
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.
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.
}
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.
}
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.
}
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("http://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();
}
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.
}
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. 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,
there is no guarantee that the thread will end. Therefore, before any
redirection or transfer of processing can occur, you must dispose of 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("http://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();
}
Because a
using clause instructs the runtime to create a
finally block, whenever you use
Response.Redirect within a
using clause, ensure that objects are disposed properly. The following code example shows how you can do this.
using (SPSite oSPSite = new SPSite("http://server"))
using (SPWeb oSPWeb = oSPSite.OpenWeb(..))
{
if (bDoRedirection)
Response.Redirect("newpage.aspx");
}
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.
Good Coding Practice #1
Explicitly Disposing
void CreatingSPSiteExplicitDisposeNoLeak()
{
SPSite siteCollection = null;
try
{
siteCollection = new SPSite("http://moss");
}
finally
{
if (siteCollection != null)
siteCollection.Dispose();
}
}
Good Coding Practice #2
Automatically Disposing
CreatingSPSiteWithAutomaticDisposeNoLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
} // SPSite object siteCollection.Dispose() is called automatically.
}
-
Dispose of items created by SharePoint methods that return other SPWeb objects (such as OpenWeb).
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.
}
-
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 of 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, Windows SharePoint Services enumerates over
this list after page completion to dispose of 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 of any
SPSite object returned from the
SPSiteCollection.Add method.
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.
}
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.
}
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 samples demonstrate improper disposal of the
SPSite object.
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.
}
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.
}
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.
}
Good Coding Practice #2
Using ForEach Loop
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.
}
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 of any
SPWeb object returned from
SPSite.AllWebs.Add.
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.
}
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.
}
SPWebCollection.Add Method
The
SPWebCollection.Add method creates and returns an
SPWeb object that needs to be disposed.
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 of innerWeb.
// innerWeb leak.
} // SPWeb object outerWeb.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
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.
}
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.
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.
}
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.
}
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.
}
SPSite.OpenWeb and SPSite. SelfServiceCreateSite Methods
The
OpenWeb method and
SelfServiceCreateSite method (all signatures) create an
SPWeb object and return it to the caller. This new object is not stored in the
SPSite object and is not disposed of anywhere in the
SPSite class. For this reason, you should dispose of 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.
}
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.
}
SPSite.RootWeb Property
An earlier version of this article 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 the SharePoint framework.
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.
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's obtained from SPContext.Current.Site.
}
Microsoft.Office.Server.UserProfiles.PersonalSite (Office SharePoint Server 2007 only)
The
Microsoft.Office.Server.UserProfiles.PersonalSite returns an
SPSite object that must be disposed.
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.
}
}
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)
{
// ...
}
}
}
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.
}
}
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)
{
// ...
}
}
}
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)
{
// ...
}
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.
// ...
}
SPWeb Objects
This section describes the situations in which
SPWeb objects are returned and may need to be disposed of.
SPWeb.ParentWeb Property
Updated Guidance
An earlier version of this article 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 the SharePoint framework.
Good Coding Practice
using (SPSite site = new SPSite(http://localhost))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists["Announcements"];
SPWeb parentWeb = list.ParentWeb; //No explicit dispose required.
}
}
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.
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.
}
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.
}
SPWeb.Webs.Add
The
SPWeb.Webs.Add method (or
Add) creates and returns a new
SPWeb object. You should dispose of any
SPWeb object returned from this method call.
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.
}
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.
}
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);
}
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);
}
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();
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();
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);
}
}
}
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 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 in Office SharePoint Server 2007, this remains a concern when migrating legacy code.
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.
}
Good Coding Practice
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;
}
}
SPControl.GetContextSite and SPControl.GetContextWeb Methods
If the object is obtained from the SharePoint context objects (
GetContextSite method and
GetContextWeb method), 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 of the objects properly.
You should still dispose of 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.
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.
}
Good Coding Practice
void SPControlBestPractice()
{
SPSite siteCollection = SPControl.GetContextSite(Context);
SPWeb web = SPControl.GetContextWeb(Context);
// Do NOT call Dispose().
}
Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager
The
SPLimitedWebPartManager class contains a reference to an internal
SPWeb object that must be disposed.
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.
}
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.
}
Microsoft.SharePoint.Publishing.PublishingWeb (SharePoint Server 2007 Only)
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.
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.
}
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.
}
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.PublishingWebCollection.GetPublishingWebs. For a code example, see the GetPublishingWebs method in the Microsoft Office SharePoint Server 2007 SDK. 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.
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.
}
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.
}
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;
}
}
}
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 Windows SharePoint Services custom code.