Thursday, March 7, 2013

Recommended Practices for disposing objects in SharePoint Development









If you don’t properly dispose of objects in the SharePoint object model that implement
IDisposable, you will have memory usage problems in your application. Under heavy
load, SharePoint may perform poorly or even exit when memory allocation fails. So
it is critical to properly dispose of these IDisposable objects. The objects to
be most careful of are SPSite and SPWeb, which must be disposed of because they
consume large amounts of unmanaged memory.


But I Thought Garbage Collection Took Care of Memory Management?
You might wonder why you must dispose of these objects yourself and why garbage collection doesn’t just take care of these things. The answer is that an object like SPSite uses a mix of managed and unmanaged code.
The memory usage of the managed side of SPSite is monitored by the .NET garbage collector, and when enough memory is used by the managed code, the garbage collector will kick in. The problem is that the .NET garbage collector doesn’t watch the unmanaged code’s use of memory and the unmanaged memory use is much greater than the managed memory use. So you
can quickly run out of memory on the unmanaged side without .NET ever feeling like it needs to do a garbage collection.
How to spot the problem?
  1. The memory usage of 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.)
  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?
  4. Does your system use custom or third-party Web Parts or custom applications?

1st Approach

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. For more information on this click here:
2nd Approach:

Microsoft provides a tool to help you detect and track down objects you aren’t disposing of properly called the SharePoint Dispose Checker tool. That tool is found here: http://code.msdn.microsoft.com/SPDisposeCheck

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 method

The basic idea behind using the Dispose method is you call it on an IDisposable object when you are done with it. At the point you call Dispose on the object the managed and unmanaged memory associated with the object is reclaimed. The object is also no longer usable after you call Dispose on it—any subsequent calls to the object will result in an error.

Example

SPSite mySite = new SPSite("http://mySite");
//do something with mysite
mySite.Dispose(); //disposes of memory used by mysite
//now don’t use mySite object as it will throw error

The using Clause

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

Example

String str;

using(SPSite oSPsite = new SPSite("http://Myserver"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
str = oSPWeb.Title;
str = oSPWeb.Url;
}
}

It also makes your code more clear and makes it impossible for you to accidentally call into the object after it has been disposed because it also goes out of scope when you are done with it.

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.
There are cases in your code where you may be creating objects that are IDisposable that may not be as obvious. For example, you may have a foreach loop that iterates over a collection that returns SPSite or SPWeb objects. For these cases you use a try, finally block in the iterator to ensure that each IDisposable object created in the foreach loop is disposed of properly.
Example
using (SPSite spSite = new SPSite('http://mysite/'))
{
foreach (SPWeb spweb in spSite.AllWebs)
{
try
{
Console.WriteLine(spweb.Name);
}
finally
{
if (spweb != null)
{
spweb.Dispose();
}
}
}
}



Whenever you wish to free memory resources, call Dispose method, instead of the Close method , internally Dispose method calls Close method itself, the reason being that standard .NET Framework process calls the Dispose method to free memory resources associated with object.
Thankfully Microsoft has recently launched Sharepoint Dispose Checker utility. It will ensure that you have properly disposed objects so that you aren’t consuming more memory. All developers should run this utility after they use Sharepoint object model. Click here to download SPDispose Utility.

Best Practice #1: Use the Using Clause for all Sharepoint objects that implement the IDisposable interface
C# Coding snippet

String str;

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

Best Practice #2: Use the try/catch/finally blocks. When you use try blocks, it is important to add a finally block to ensure that all objects are disposed off properly:

C# Coding snippet

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

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

str = oSPWeb.Title;
}
catch(Exception e)
{
}
finally
{
if (oSPWeb != null)
  oSPWeb.Dispose();

if (oSPSite != null)
   oSPSite.Dispose();
}

Best Practice #3: Using Response.Redirect in the try block will never execute the finally block, so it is important, to dispose off all objects before the redirection occurs:

C# Coding snippet
String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;
bool bDoRedirection = true;

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();
}

Best Practice #4: Whenever you create an object with a new operation, the creating application must dispose it off

C# Coding snippet

SPSite oSPSite = new SPSite("http://server");

... additional processing on SPSite ...

oSPSite.Dispose();

alternatively, this is a better approach

C# Coding snippet

using(SPSite oSPSite = new SPSite("http://server"))
{
... additional processing on SPSite ...
}

Best Practice #5: For Site.OpenWeb method, you need to dispose it off explicitly.

C# Coding snippet

String str;
SPSite oSPSite = new SPSite("http://server");
SPWeb oSPWeb = oSPSite.OpenWeb();

str = oSPWeb.Title;
str = oSPWeb.Url;

... additional processing on SPWeb ...

oSPWeb.Dispose();
oSPSite.Dispose();

Alternatively you may use the Using clause too for better readability and automatic disposition of objects:

C# Coding snippet

String str;
using(SPSite oSPSite = new SPSite("http://server"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
    str = oSPWeb.Title;
    str = oSPWeb.Url;

    ... additional processing on SPWeb ...
}
}

Best Practice #6: An exception to the rule is that One should not explicitly dispose SPSite.RootWeb, as it is automatically disposed off.

Similary one should not explicity dispose SPContext.Current.Site and SPContext.Current.Web as they are handles automatically by Sharepoint and .NET framework.

C# Bad Coding Practice Snippet:

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

C# Good Coding Practice Snippet:

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

Best Practice #7: SPControl.GetContextSite(Context) and GetContextWeb(Context) return SPSite and SPWeb respectively, they do not need an explicit call to Dispose(), they will be disposed automatically

C# Bad Coding Practice Snippet:

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

C# Good Coding Practice Snippet:

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

Best Practice #8: SPWeb.ParentWeb  returns SPWeb, and needs to be disposed off explicitly using either Dispose() or the using clause

C# Bad Coding Practice Snippet:
void ParentWebLeak()
{
 using (SPSite siteCollection = new SPSite("http://moss"))
 {
     using (SPWeb outerWeb = siteCollection.OpenWeb())
     {
         SPWeb parentWeb = outerWeb.ParentWeb; // Internal reference to SPWeb parentWeb
         string sTitle = parentWeb.Title;
         string sUrl = parentWeb.Url;
         // SPWeb object parentWeb leaked
     } // SPWeb object outerWeb.Dispose() automatically called
 }  // SPSite object siteCollection.Dispose() automatically called
}

C# Good Coding Practice Snippet:
void ParentWebBestPractice()
{
 using (SPSite siteCollection = new SPSite("http://moss"))
 {
     using (SPWeb outerWeb = siteCollection.OpenWeb())
     {
         using (SPWeb parentWeb = outerWeb.ParentWeb) // Internal reference to SPWeb parentWeb
         {
             string sTitle = parentWeb.Title;
             string sUrl = parentWeb.Url;
         } // SPWeb object parentWeb.Dispose() automatically called
     } // SPWeb object outerWeb.Dispose() automatically called
 }  // SPSite object siteCollection.Dispose() automatically called
}

Best Practice #9: SPWeb.Webs returns an SPWeb object, practically used in drilling down sub-sites within a Site

C# Bad Coding Practice Snippet:
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
}

C# Good Coding Practice Snippet:
void WebsBestPractice()
{
 using (SPSite siteCollection = new SPSite("http://moss"))
 {
     using (SPWeb outerWeb = siteCollection.OpenWeb())
     {
         foreach (SPWeb innerWeb in outerWeb.Webs)
         {
             innerWeb.Dispose();
         }
     } // SPWeb object outerWeb.Dispose() automatically called
 }  // SPSite object siteCollection.Dispose() automatically called
}

Best Practice #10: AllWebs[] Indexer returns SPWeb
object that needs to be disposed to avoid aggregation of memory which
can lead to memory pressure when running on a site collection with
large number of sub sites. Practically used in Enumerating or iterating all webs in a site collection.

C# Bad Coding Practice Snippet:

void AllWebsForEachLeakBestPractices()
{
 using (SPSite siteCollection = new SPSite("http://moss"))
 {
     using (SPWeb outerWeb = siteCollection.OpenWeb())
     {
         foreach (SPWeb innerWeb in siteCollection.AllWebs)
         {
              // Explicit Dispose must be called to avoid aggregation of memory
         }
     } // SPWeb object outerWeb.Dispose() automatically called
 }  // SPSite object siteCollection.Dispose() automatically called
}

C# Good Coding Practice Snippet:

void AllWebsForEachLeakBestPractices()
{
 using (SPSite siteCollection = new SPSite("http://moss"))
 {
     using (SPWeb outerWeb = siteCollection.OpenWeb())
     {
         foreach (SPWeb innerWeb in siteCollection.AllWebs)
         {
             innerWeb.Dispose();   // Explicit Dispose must be called to avoid aggregation of memory
         }
     } // SPWeb object outerWeb.Dispose() automatically called
 }  // SPSite object siteCollection.Dispose() automatically called
}

C# Bad Coding Practice Snippet:

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

C# Good Coding Practice Snippet:

void AllWebsIndexerBestPractice()
{
 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
}

Best Practice #11: AllWebs.Add() returns a instance of SPWeb object which needs to be disposed.

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

C# Good Coding Practice Snippet:
void AllWebsAddBestPractice()
{
 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
}

Best Practice #12: OpenWeb() returns a SPWeb object which needs to be disposed.

C# Bad Coding Practice Snippet:
void OpenWebLeak()
{
 using (SPWeb web = new SPSite(SPContext.Current.Web.Url).OpenWeb())
 {
     // SPSite leaked !
 } // SPWeb object web.Dispose() automatically called
}

C# Good Coding Practice Snippet:
void OpenWebBestPractice()
{
 using (SPSite siteCollection = new SPSite("http://moss"))
 {
     using (SPWeb web = siteCollection.OpenWeb())
     {
     } // SPWeb object web.Dispose() automatically called
 }  // SPSite object siteCollection.Dispose() automatically called
}

Best Practice #13:new SPSite() - Instantiating SPSite objects with the new operator needs to be disposed.

C# Bad Coding Practice Snippet:
void CreatingSPSiteLeak()
{
 SPSite siteCollection = new SPSite("http://moss");
 // siteCollection leaked
}

C# Good Coding Practice Snippet:
void CreatingSPSiteExplicitDisposeBestPractice()
{
 SPSite siteCollection = new SPSite("http://moss");
 siteCollection.Dispose();
}

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

No comments:

Post a Comment