Although SharePoint objects (SPSite, SPWeb, etc. ) are essentially managed code, they internally invoke unmanaged code (COM objects to handle database connections). As a direct result, if these objects are not explicitly disposed correctly, they will cause memory leaks that may eventually cause out of memory errors or make the application unstable. it is estimated that each un-disposed instance of SPSite or SPWeb consumes 1 to 2 MB of unmanaged resources.
A background about Memory Management:
In a 32 bit machine, the maximum amount of memory that a process can address is 2GB. This memory is partitioned into chunks of 64MB (on average, as this number slightly changes between the different versions of .NET Frameworks). Being in chunks, it means that a 3KB DLL will still occupy a 64MB slot. For ASP.NET applications (including SharePoint), their memory ceiling is even lower than that 2GB since they live inside the w3wp process. The w3wp process loads some ISAPI DLLs and some IIS related DLLs in addition to the ASP.NET application. This explains why, for example, a server can throw out of memory exceptions while it has more than 5GB of free RAM. A good analogy can be found at (http://blogs.msdn.com/tess/archive/2006/09/06/net-memory-usage-a-restaurant-analogy.aspx)
Back to SharePoint, it means that all un-disposed objects are occupying memory within the 2GB w3wp and will eventually cause the application to crash, become unstable or starts throwing exceptions. Usually an application pool recycle or iisreset will be needed in order to get rid of these objects. Another major problem with un-disposed objects in SharePoint is that the unmanaged COM objects that are not disposed and that remain in memory are responsible for connecting to the back end database. By remaining un-disposed, the connection to the database remain open. Other that affecting performance, a machine can only have a certain number of connections open to the database (per machine overall , not per process). If the number of open connections remaining open reaches this limit, other processes that need to connect to the database will not be able to open new connections and will start to throw exceptions or behave strangely. This applies to 64 bits machines as well, even though memory might not be clogged, but connections will be clogged.
SharePoint v3.0 ULS Logging systems logs the SP objects that are not disposed at the end of the the thread. In order to pinpoint what code is causing such memory leaks, you need to:
- Set the Logging level to Warning / Medium
- 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 will add the stack trace of the thread that is not disposing SP objects correctly to the logs, thus helping developers find out which part of the code is the problem. In the ULS logs, you will find something similar to: General 8l1n High An SPRequest object was not disposed before the end of this thread.
Some general recommendations to avoid running out of memory:
- Do not force more than one web application to use the same application pool. This will mean that more applications will be squeezed within the same 2GB that this process can address.
- Do not run production code in debug mode. Running in debug mode means that each page’s code behind will be compiled into a separate DLL and therefore heavily fragmenting the memory. (use debug=false)
- If possible, move to a 64bit architecture.
Some recommendations for building custom code for SharePoint (controls, web parts, Event handlers, etc.)
- First and foremost, Do not build your own controls unless you have to. Try first to customize out-of-the-box components.
- If you have to use Code, check it with the SPDispose Check tool (http://code.msdn.microsoft.com/SPDisposeCheck) this tool will review the Code to help developers identify objects that are not correctly disposed.
- Follow the best practices for using SP objects as Listed in http://msdn.microsoft.com/en-us/library/aa973248.aspx. This pretty much summarizes how to dispose SP objects correctly.
- Common alternatives to explicitly calling Dispose() include using the SP object inside a try/catch/finally or using a “using” block. In fact the MSIL transforms using blocks to try/finally. Please note that you need a using block per object. for example, if you create SPSite then SPWeb, you need to call then like this
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 Dispose objects referenced from SPContext. These objects are managed by the SharePoint runtime and should not be disposed from your code. For example, do NOT dispose SPSite object you get through SPContext.Current.Site.
- Same for references obtained through list handlers like SPListEventProperties.Web.
- Do not nest the creation of SP objects in one line as it will become tricky when disposing them. for example:
using (SPWeb web = new SPSite(http://shareoint).OpenWeb())as in such case you wont have reference to the SPSite object in order to dispose it
- When looping through Web.Webs for example, make sure to dispose every SPWeb created inside the loop.
- Do NOT store SPSite or SPWeb objects in session or application variables. You can cache or Store the GUID, URL, whatever you can use to rebuild the SP object. You cannot cache the SP object itself.