There are several articles around discussing the dispose of SPWeb and SPSite objects, e.g.:
- SharePoint 2007 and WSS 3.0 Dispose Patterns by Example from Roger Lamb
- Best Practices: Using Disposable Windows SharePoint Services Objects from Scott Harris and Mike Ammerlaan
- Best Practices: Common Coding Issues When Using the SharePoint Object Model from Scott Harris and Mike Ammerlaan
And also in a couple of articles I wrote myself:
- Dealing with Memory Pressure problems in MOSS/WSS
- Troubleshooting SPSite/SPWeb leaks in WSS v3 and MOSS 2007
So we all should know how important it is to actually dispose all SPSite and SPWeb objects we create in our code to avoid problems.
Today I would like to highlight some additional aspects which might not be that obvious.
1) What happens if a SPWeb object is not disposed?
As discussed in my earlier article each SPWeb and SPSite object holds a reference to an SPRequest object which holds a reference to a SharePoint COM object that is responsible to communicate with the backend SQL server.
Disposing a SPWeb object will not actually remove the SPWeb object from memory (actually the .NET framework does not allow to remove any object from memory in a deterministic way) but it will call a method of the SPWeb object which causes the COM object to close the connection to the SQL server and to release its allocated memory.
That means the connection to the backend SQL server will remain open from the moment the SPRequest object has been created till the SPWeb object is disposed.
After the dispose only the small managed SPWeb and SPRequest objects remain in memory which do not create a big memory overhead. They are removed by the dot net framework through garbage collection later as with any other managed object as soon as no references to the object exist any more.
In case that the SPWeb object is not disposed when it is no longer used, then the connection to the SQL server will stay open and the memory allocated by the COM object will stay in memory as the COM object has not been asked to close the connection and to release the memory.
Each connection to the database requires a tcp port for the communication. Per default only 5000-1023 = 3977 ports are available (see the MaxUserPort registry setting for details). Which means on a single machine per default you cannot have more than 3977 open connections to other applications. Be aware that this is the number for all processes. Not just a single application pool.
So beside the fact that not disposing SPWeb and SPSite will lead to a higher memory consumption which finally can lead to out of memory exceptions the machine might also run out of TCP ports which would lead to lots of other problems - potentially also in other applications on the same server.
2) Does this mean that the allocated space will remain in memory forever if the SPWeb/SPSite object is not disposed?
With WSS 2.0 (and SPS 2003 as it sits on top of WSS 2.0) the answer would be yes - at least under heavy load. Only an application pool recycle would allow to recover from such a situation. The reason for this is that under heavy load the finalizer will not execute in a timely fashion and the server can quickly get into a memory pressure situation, so a proper disposal is important.
With WSS 3.0 (and MOSS 2007 as it sits on top of WSS 3.0) the answer is actually no. There are two different mechanisms to mitigate the impact of custom code that does not properly dispose SPSite and SPWeb objects.
Method 1: SPWeb tracking by SPSite
Whenever an SPWeb object is created the SPSite object associated with the new SPWeb object automatically adds the new SPWeb object to an internal list. This works for all SPWeb object creations - be it through SPWeb.OpenWeb, SPSite.RootWeb, SPWeb.ParentWeb or any other method that directly or indirectly creates an SPWeb object.
When the SPSite object finally gets disposed it will loop through the list and ensure that all SPWeb objects associated with this SPSite object also get disposed.
This might lead to the assumption that just disposing all SPSite objects rather than disposing each individual SPWeb object would be sufficient to minimize the memory usage. In theory this is correct. But it will postpone the dispose of the SPWeb object to the time when the SPSite object gets disposed. So the dispose does not happen right after the SPWeb object is no longer used but much later.
Especially when you have a loop like the following an explicit dispose of the SPWeb object is mandatory to minimize the number of undisposed SPWeb objects in the process:
foreach (SPWeb web in site.AllWebs)
// do something with the web object
web.dispose(); // <-- this is mandatory!
Method 2: SPRequest tracking
The previous Method is the savety net for code that misses to dispose SPWeb object. But it does not protect against missing SPSite object disposal. To protect against missing SPSite object disposal WSS 3.0 tracks each created SPRequest object and keeps it in a list together with the information on which thread it was created till the creating object gets disposed. Whenever a thread finishes WSS verfies if there are any SPRequest objects in it's list. If it finds any it ensures that the COM object bound ot this SPRequest object closes the connection to the SQL server and that the memory allocated by the COM component is released.
In addition it writes the an entry into the ULS log of the following type:
05/01/2007 12:58:47.31 w3wp.exe (0x105C) 0x09A8 Windows SharePoint Services General 8l1n High An SPRequest object was not disposed before the end of this thread...
(see the following article to get more details about this message)
Be aware that threads are often reused for multiple requests. That means that a missing dispose for a SPSite object will cause the resources allocated by the COM component to remain much longer in memory as if a dispose for a SPWeb object was missing where the SPSite object was properly disposed.
As we can see in WSS 3.0 additional mechanisms have been added to ensure that incorrectly coded components don't have the same impact on the availability of the system as in WSS 2.0. These mechanisms help to fix issues where single objects have not been disposed. But if code that misses to dispose SPWeb or SPSite objects get called in a repeated manner it can still lead to high memory consumption up to out of memory problems.
3) When should SPWeb and SPSite objects be disposed?
The answer for this is: as soon as it is no longer needed. That sounds simple - but sometimes this is not easy to determine.
Ok, for code like the following the answer is simple:
foreach (SPWeb web in site.AllWebs)
// do something with the web object
web.dispose(); // <-- this is mandatory!
Here the dispose needs to be the last statement of the loop.
But what if you have code like the following:
SPSite site = new SPSite();
SPWeb web = site.RootWeb;// Position 1
SPList list = web.Lists["MyList"]
// Position 2
...code that uses the list object...
// Position 3
Where should you add the dispose for the SPSite and SPWeb object? When are the objects no longer used?
Ok, if you have read the previous paragraph you will notice that a dispose for the SPSite object in position 1 will automatically dispose all SPWeb objects bound to the SPSite object. So it will dispose the SPWeb object used after Position 1 to access the list. So that would mean Position 1 is not the right place.
What about Position 2? Can we dispose the SPSite object here? Or at least SPWeb object? The code does not show a usage of any of these objects. So a dispose seems to be save, right?
Actually some internal methods of the SPList object access the SPWeb object it belongs to to perform certain operations. So we cannot dispose the SPWeb object here without side effects. And that also means that we cannot dispose the SPSite object here as it would automatically dispose the SPWeb objects.
That means that we have to dispose the SPWeb and SPSite object at Position 3 and not earlier.
What would happen if we would add code to dispose the SPWeb or SPSite object earlier? In some rare cases you will see an exception. But in most cases you would not see a problem with your code! Everything would work fine - but why?
The reason is that many methods silently recreate the SPWeb objects if it has been disposed. That means: even though you disposed the object in your code - a couple of code lines later it suddenly reappears and would have to be disposed again.
So the answer to the question is: You should dispose a SPWeb or SPSite object after the last access to a child object of this object. Be aware that a sub site (SPWeb) is not a child object. But (e.g.) a list or a folder or list item is a child object for this scenario.
4) When should I dispose SPSite.RootWeb?
When SPSite.RootWeb is first accessed it creates an SPWeb object using SPSite.OpenWeb and stores a reference in an internal variable. Further accesses to SPSite.RootWeb are then satisfied by returning a reference to the earlier created object. In case the RootWeb object has been disposed it will create a new one using OpenWeb as for the initial access.
That means we have a single SPWeb object per SPSite object even if we access SPSite.RootWeb multiple times. Creating a SPWeb object is an expensive operation as it requires the instantiation of a COM object, a database communication to download the info about the SPWeb object and so on. That means disposing the RootWeb object after every single access to it can affect the performance of the site - especially if you have many different places in your project that do the same for the same SPSite object. So ensure to dispose the RootWeb object only after the last access to it in your code whereever the logic of the code allows to determine this.
Also please do not dispose the RootWeb property of SPContext.Current.Site. This object is used in many different places in SharePoint and you should not dispose it. It will automatically be cleaned up when the request finishes.
5) Can I cache SharePoint objects in session variables?
That question is a little bit off topic but as it is related to the underlaying SPRequest objects I decided to cover it here.
As we discussed in 2) WSS 3.0 monitors the thread termination and releases all SPRequest objects allocated on during the livetime of this specific thread. When storing SharePoint objects in session variables then the objects will usually be accessed on different threads - which is not allowed. One of the reasons is that the COM resources for the objects will get released when the thread that created the objects terminates - independent if other threads still use it.
What you can do is to store the meta information of SharePoint objects (like the URL, the GUID, ...) in session variables. But not the SharePoint objects themselves.
6) Is it also important to dispose SPWeb objects on 64-bit machines with lots of memory?
Even on a 64-bit machine you have the same limitation regarding user ports (see paragraph 1). It is possible to increase the number of user ports in the registry but there is still a maximum size which could be reached with large sites or multiple application pools if the code does not properly handle dispose of SPWeb objects.
7) Dispose of objects which do not belong to the method
In general SPSite and SPWeb objects should be disposed in the same method they get allocated. That's the best method to ensure that no disposed can be missed and to ensure that a dispose does not occur for an object that will be used later.
I have seen several cases where custom code disposed SPWeb and SPSite objects incorrectly.
A very common scenario is the following:
using (SPSite mySite = SPContext.Current.Site)
Using "using" statements is a very nice method to ensure that at the end the object being used gets properly disposed. The problem in the code above is that SPContext.Current.Site and SPContext.Current.Web are not allowed to be disposed! Using successfully hides the dispose statement here. "Using" allows a nice structuring of the code so users often use it without thinking about the side effects.
If you plan to use a using statement with SPContext you need to code it like this:
using (SPSite mySite = new SPSite(SPContext.Current.Site.ID))
This method will ensure that a new independent SPSite object is created which you then can dispose without side effects on other code using the SPSite object bound to the current SPContext object.
Another common error is to dispose an SPSite or SPWeb object in a event receiver:
public override void ItemCheckingOut(SPItemEventProperties properties)
// incorrect dispose of SPWeb
using (SPWeb web = properties.ListItem.Web)
// incorrect dispose of SPSite
using (SPWeb site = properties.ListItem.Web.Site)
After executing our code other event receivers will receive an object where the underlaying SPWeb and/or SPSite object has been disposed. This can lead to exceptions.
You need to ensure that you only dispose SPSite and SPWeb objects that your code owns.
[Update Dec 7th, 2008] Please also have a look at the following article which is actually a response to this article and has many additional samples for the things I mentioned here.