WSS 3.0对象模型中的对象就是一种接口,开发人员可以通过调用它来读写数据。

WSS 3.0对象模型中的对象包含的对象都实现了IDisposable接口,你必须谨慎地使用它们,以免它们长时间的停留在.NET Framework的内存中。你在使用完这些对象后,最好能显示的释放这些对象所占用的资源。

如果你大量使用SP Objects,比如说自定义的Web Part, 下面这些不正常的现象都是资源对象没被Disposed引起的。

1/ WSS Application Pool频繁地资源回收,特别是在高峰时间

2/ 在Debugger的时候,Application死掉

3/ IIS 进程占着高CPU

4/ System and Application performance 差

本文告诉你用合适的方法处理和释放SharePoint对象。

一些WSS对象,主要是SPSite class和SPWeb class,被创建成可托管的对象,然而,这些对象使用的是非托管的代码和内存来执行大部分工作。托管的部分很小,非托管的部分很大。由于该对象托管的很小,它不会对(垃圾回收器)Garbage Collector的内存有压力,Garbage Collector不会主动及时地释放该对象所占的资源。而该对象非托管的资源由于占了很多内存就会导致之前描述的几种不正常的行为。所以当你的Application调用实现IDisposable的对象时,在使用完该对象时,你必须主动地释放该对象。你不该依靠Garbage Collector自动的回收内存资源。

我们可以用以下几种方法来确保我们的资源在使用后及时得到释放。

1/ Dispose Method

2/ using clause

3/ try, catch and finally blocks

Dispose Vs Close method的使用

SPWeb object and SPSite object在功能上是一样的,Dispose方法只是简单的调用了对象的Close方法。基于以下理由,我推荐大家使用Dispose而少用Close.

1/ SPWeb and SPSite 实现了IDisposable接口,标准的.NET Framework进程调用Dispose方法可以从内存中释放该对象的一切资源。

2/ 也保证你之后的Release能正常的运行

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

The following code provides an example.

C#
String str;

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

try/catch/finally Blocks
The following is a recommendation of how to code try/catch/finally blocks. When you use try blocks, it is important to add a finally block to ensure that all objects are properly disposed of.

Any code that is ultimately contained within a try { } catch { } block should have a governing finally clause, which ensures that the respective objects are disposed of. These try { } catch { } blocks can exist outside of the function where you use SharePoint objects. In those cases, you should consider using try/catch/finally in the methods to wrap your usages of the SharePoint object model directly.

C#
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();
}
Calling Response.RedirectWILL NOT execute the finally block. Therefore, before any redirection or transfer of processing can occur, you must dispose of the objects. For example, if your code must redirect, implement it in a way similar to the following.

C#
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();
}
Recommendations to Reduce Long-Term Object Retention
To reduce long-term retention of Windows SharePoint Services objects, we offer the following general recommendations.

If you create the object with a new operator, the creating application must dispose of it.

Good Coding Practice #1

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

 ... additional processing on SPSite ...

oSPSite.Dispose();

Good Coding Practice #2

C#
using (SPSite oSPSite = new SPSite("http://server/"))
{
   ... additional processing on SPSite ...
}
SharePoint methods that return other SPWeb objects (such as SPSite.OpenWeb) create new items and should be disposed of.

Good Coding Practice #1

C#
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();

Good Coding Practice #2

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

       ... additional processing on SPWeb ...
   }

The SPSite.RootWeb property and SPWeb.ParentWeb property create new objects and assign them to a local member variable. Each succeeding access to the property uses the item assigned to the member variable. As a result, you can call the property as often as you want without creating multiple items. However, you must call the Dispose method of each property when you finish using it. In the following example, you can use the RootWeb and ParentWeb properties in the same way.

Good Coding Practice #1

C#
String str;
SPSite oSPSite = new SPSite("http://server/");

str = oSPSite.RootWeb.Title;
str = oSPSite.RootWeb.Url;

 ... additional processing on RootWeb ...

oSPSite.RootWeb.Dispose();
oSPSite.Dispose();

Good Coding Practice #2

C#
String str;
using(SPSite oSPSite = new SPSite("http://server/"))
{
   str = oSPSite.RootWeb.Title;
   str = oSPSite.RootWeb.Url;

   ... additional processing on RootWeb ...

oSPSite.RootWeb.Dispose();
}

Good Coding Practice #3

C#
String str;
using(SPSite oSPSite = new SPSite("http://server/"))
{
   using(SPWeb oRootWeb = oSPSite.RootWeb)
   {
      str = oRootWeb.Title;
      str = oRootWeb.Url;
   … additional processing on RootWeb …
   }
}

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

In general, any time a calling application uses the new SPSite() constructors (any signature), it should call the SPSite.Dispose() method when it is finished using the object. If the SPSite object is obtained from SPControl.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.

Good Coding Practice #1
C#
SPGlobalAdmin oSPGlobalAdmin    = new SPGlobalAdmin();
SPSiteCollection aSites         = oSPGlobalAdmin.VirtualServers[0].Sites;

SPSite oSPSite = aSites.Add( ... );

 ... Process the site info ...

oSPSite.Dispose();
oSPGlobalAdmin.Dispose();
Good Coding Practice #2
C#
SPGlobalAdmin oSPGlobalAdmin    = new SPGlobalAdmin();
SPSiteCollection aSites         = oSPGlobalAdmin.VirtualServers[0].Sites;

using(SPSite oSPSite = aSites.Add( ... ))
{
   ... Process the site info ...
}

oSPGlobalAdmin.Dispose();
SPSiteCollection [ ] Index Operator
The SPSiteCollection [] index operator returns a new SPSite object for each access. An SPSite instance is created even if that object has already been accessed. The following code samples demonstrate improper disposal of the SPSite object.

Bad Coding Practice #1
C#
int i;
SPSite oSPSite;

SPGlobalAdmin oSPGlobalAdmin    = new SPGlobalAdmin();
SPSiteCollection aSites         = oSPGlobalAdmin.VirtualServers[0].Sites;

for (i = 0;i < aSites.Count;i++)
{
   oSPSite = aSites[i];
   BuildTableRow(oDisplayTable, "Site", oSPSite.Url);
}

oSPGlobalAdmin.Dispose();
Bad Coding Practice #2
C#
SPGlobalAdmin oSPGlobalAdmin    = new SPGlobalAdmin();
SPSiteCollection aSites         = oSPGlobalAdmin.VirtualServers[0].Sites;

foreach(SPSite oSPSite in aSites)
{
   BuildTableRow(oDisplayTable, "Site", oSPSite.Url);
}

oSPGlobalAdmin.Dispose();
The recommended fix is to place a Dispose() at the end of each loop.

Good Coding Practice #1
C#
int i;
SPSite oSPSite;

SPGlobalAdmin oSPGlobalAdmin    = new SPGlobalAdmin();
SPSiteCollection aSites         = oSPGlobalAdmin.VirtualServers[0].Sites;

for(i = 0;i < aSites.Count;i++)
{
   oSPSite = aSites[i];
   BuildTableRow(oDisplayTable, "Site", oSPSite.Url);
   oSPSite.Dispose();
}

oSPGlobalAdmin.Dispose();
Good Coding Practice #2
C#
SPGlobalAdmin oSPGlobalAdmin    = new SPGlobalAdmin();
SPSiteCollection aSites         = oSPGlobalAdmin.VirtualServers[0].Sites;

foreach(SPSite oSPSite in aSites)
{
   BuildTableRow(oDisplayTable, "Site", oSPSite.Url);
   oSPSite.Dispose();
}

oSPGlobalAdmin.Dispose();
Good Coding Practice #3
C#
int i;

SPGlobalAdmin oSPGlobalAdmin    = new SPGlobalAdmin();
SPSiteCollection aSites         = oSPGlobalAdmin.VirtualServers[0].Sites;

for(i = 0;i < aSites.Count;i ++)
{
   using(SPSite oSPSite = aSites[i])
   {
      BuildTableRow(oDisplayTable, "Site", oSPSite.Url);
   }
}

oSPGlobalAdmin.Dispose();
SPSite.AllWebs Property (SPWebCollection)
This section describes the methods, properties, or operators in the SPSite.AllWebs property collection that require the SPWeb object to be closed after access.

SPSites.AllWebs.Add Method
The SPSite.AllWebs.Add method(or SPWebCollection.Add) creates and returns an SPWeb object. You should dispose of any SPWeb object returned from SPSite.AllWebs.Add.

Good Coding Practice #1
C#
SPWeb oSPWeb;
SPSite oSPSite = SPControl.GetContextSite(Context);

oSPWeb = oSPSite.AllWebs.Add( ... );

 ... Process the SPWeb info ...

oSPWeb.Dispose();
Good Coding Practice #2
C#
SPSite oSPSite = SPControl.GetContextSite(Context);

using(SPWeb oSPWeb = oSPSite.AllWebs.Add( ... ))
{
   ... Process the SPWeb info ...
}
Note: 
The SPSite object was obtained from the GetContextSite()method and does not need to be disposed of.
 

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 has already been accessed. If not properly closed, the following code samples leave an SPWeb object in the .NET Framework garbage collector.

Bad Coding Practice #1
C#
int i;

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

for(i=0;i < oSPSite.AllWebs.Count; i++)
{
   oSPWeb = oSPSite.AllWebs[i];
   BuildTableRow(oDisplayTable, "Web", oSPWeb.Title);
}
Bad Coding Practice #2
C#
SPSite oSPSite = SPControl.GetContextSite(Context);

foreach(SPWeb oSPWeb in oSPSite.AllWebs)
{
   BuildTableRow(oDisplayTable, "Web", oSPWeb.Title);
}
The recommended way to fix this is to place Dispose() at the end of each loop.

Good Coding Practice #1
C#
int i;

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

for(i = 0;i < oSPSite.AllWebs.Count; i++)
{
   oSPWeb = oSPSite.AllWebs[i];
   BuildTableRow(oDisplayTable, "Web", oSPWeb.Title);
   oSPWeb.Dispose();
}
Good Coding Practice #2
C#
SPSite oSPSite = SPControl.GetContextSite(Context);

foreach(SPWeb oSPWeb in oSPSite.AllWebs)
{
   BuildTableRow(oDisplayTable, "Web", oSPWeb.Title);
   oSPWeb.Dispose();
}
Good Coding Practice #3
C#
int i;

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

for(i = 0;i < oSPSite.AllWebs.Count; i++)
{
   using(oSPWeb = oSPSite.AllWebs[i])
   {
      BuildTableRow(oDisplayTable, "Web", oSPWeb.Title);
   }
}
SPSite.OpenWeb and SPSite. SelfServiceCreateSite Methods
The SPSite.OpenWeb method and SPSite.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.

Good Coding Practice #1
C#
SPSite oSPSite = new SPSite("http://server/");
SPWeb oSPWeb = oSPSite.OpenWeb(..);

 ... additional processing ...

oSPWeb.Dispose();
oSPSite.Dispose();
Good Coding Practice #2
C#
using(SPSite oSPSite = new SPSite("http://server/"))
{
   using(SPWeb oSPWeb = oSPSite.OpenWeb(..))
   {
      ... additional processing ...
   }
}
SPSite.LockIssue, SPSite.Owner, and SPSite.SecondaryContact Properties
The following properties reference data from the top-level Web site and use the SPSite.RootWeb property:

SPSite.LockIssue

SPSite.Owner

SPSite.SecondaryContact

For more information, see the discussion of SPSite.RootWeb. If you use any of these respective properties, you must call the Dispose method on the SPSite.RootWeb property.

Good Coding Practice #1
C#
String str;
SPSite oSPSite = new SPSite("http://server/");

str = oSPSite.LockIssue;
 
oSPSite.RootWeb.Dispose();
oSPSite.Dispose();
Good Coding Practice #2
C#
String str;
using(SPSite oSPSite = new SPSite("http://server/"))
{
   str = oSPSite.Owner;
 
   oSPSite.RootWeb.Dispose();
}
SPSite.RootWeb Property
The first time the SPSite.RootWeb property is called, the property determines whether a member variable is assigned with a non-null value. If the member variable is null, a new SPWeb object is created by calling SPSite.OpenWeb method and assigns the new SPWeb object to the member variable. The next call to the SPSite.RootWeb property simply returns the value that was stored in the member variable.

The calling application should dispose of the SPSite.RootWeb property just before disposing of the SPSite object that is using it, as shown in the following example.

Good Coding Practice #1
C#
String str;
SPSite oSPSite = new SPSite("http://server/");

str = oSPSite.RootWeb.Title;

 ... additional processing ...

oSPSite.RootWeb.Dispose();
oSPSite.Dispose();

Good Coding Practice #2
C#
String str;
using(SPSite oSPSite = new SPSite("http://server/"))
{
   str = oSPSite.RootWeb.Title;
   ... additional processing ...

   oSPSite.RootWeb.Dispose();
}
Good Coding Practice #3
C#
String str;
using(SPSite oSPSite = new SPSite("http://server/"))
{
   using(SPWeb oRootWeb = oSPSite.RootWeb)
   {
      str = oRootWeb.Title;

      ... additional processing ...
   }
}

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

SPWeb.ParentWeb Property
The first time the SPWeb.ParentWeb property is called, it determines whether a member variable is assigned with a non-null value. If the member variable is null, a new SPWeb object is created if a valid parent Web site exists by calling the OpenWeb method, and assigns the new SPWeb object to the member variable. The next call to the SPWeb.ParentWeb property simply returns the value that was stored in the member variable.

The calling application should dispose of the SPWeb.ParentWeb property just before disposing the SPWeb object that is using it. Following are examples.

Good Coding Practice #1
C#
String str;
SPSite oSPSite = new SPSite("http://server/");
SPWeb oSPWeb, oSPWebParent;

oSPWeb       = oSPSite.OpenWeb();
oSPWebParent = oSPWeb.ParentWeb;

if (oSPWebParent != null)
{
   ... additional processing ...
}

if (oSPWebParent != null)
   oSPWebParent.Dispose();

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

Good Coding Practice #2
C#
String str;
SPWeb oSPWeb, oSPWebParent;

using(SPSite oSPSite = new SPSite("http://server/"))
{
   using(SPWeb oSPWeb = oSPSite.OpenWeb())
   {
      oSPWebParent = oSPWeb.ParentWeb;

      if(oSPWebParent != null)
      {
         ... additional processing ...
      }

      If(oSPWebParent != null)
         oSPWebParent.Dispose();
   }
}
SPWeb.Webs Property (SPWebCollection)
This section describes the methods, properties, or operators in the SPWeb.Webs property collection that require disposal of the SPWeb object after access.

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.

Good Coding Practice #1
C#
SPWeb oSPWeb
SPSite oSPSite = SPControl.GetContextSite(Context);

oSPSWeb = oSPSite.AllWebs.Add( ... );

 ... Process the SPWeb info ...

oSPWeb.Dispose();
Good Coding Practice #2
C#
SPSite oSPSite = SPControl.GetContextSite(Context);

using(SPWeb oSPSWeb = oSPSite.AllWebs.Add( ... ))
{
   ... Process the SPWeb info ...
}
Note: 
The SPSite object was obtained from the GetContextSite() method and does not need to be disposed of.
 

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

Bad Coding Practice #1
C#
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
C#
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 place a close at the end of each loop.

Good Coding Practice #1
C#
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
C#
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
C#
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 Area.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.

Bad Coding Practice #1
C#
String str;

Area oArea = AreaManager.GetArea(PortalContext.Current, new Guid(AreaGiud);
str = oArea.Web.Title;   // Web is not reclaimed
str = oArea.Web.Url   // Web is not reclaimed
Good Coding Practice #1
C#
String str;

Area oArea = AreaManager.GetArea(PortalContext.Current,
   new Guid(AreaGiud);
SPWeb oSPWeb = oArea.Web;

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

oSPWeb.Dispose();
Good Coding Practice #2
C#
String str;

Area oArea = AreaManager.GetArea(PortalContext.Current, new
   Guid(AreaGiud);
using(SPWeb oSPWeb = oArea.Web)
{
   str = oSPweb.Title;
   str = oSPWeb.Url;
}
SPControl.GetContextSite and SPControl.GetContextWeb Methods
If the object is obtained from the SharePoint context objects (SPControl.GetContextSite method and SPControl.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 SPControl.GetContextSite method.

Bad Coding Practice #1
C#
SPSite oSPSite = SPControl.GetContextSite(..);

 ... additional processing ...

oSPSite.Dispose();
// Could cause problems in the SharePoint object model
Good Coding Practice #1
C#
SPSite oSPSite = SPControl.GetContextSite(..);
SPWeb oSPWeb = oSPSite.OpenWeb(..);

 ... additional processing ...

oSPWeb.Dispose();
Good Coding Practice #2
C#
SPSite oSPSite = SPControl.GetContextSite(..);
using(SPWeb oSPWeb = oSPsite.OpenWeb())
{
   ... additional processing ...
}
WebPartPage.RootWeb Property
The WebPartPage.RootWeb property is similar to the SPSite.RootWeb property in that the first time the property is called, it determines whether a member variable is assigned with a non-null value. If the member variable is null, a new SPWeb object is created by calling the SPSite.OpenWeb method; the new SPWeb object is assigned to the member variable, or the member variable is assigned to WebPartPage.Web if that SPWeb object is a top-level Web site.

The calling application should dispose of the WebPartPage.RootWeb property only if WebPartPage.IsRootWeb returns true.

Good Coding Practice #1
C#
String str;
WebPartPage oWebPartPage = new WebPartPage();

str = oWebPartPage.RootWeb.Title;

 ... additional processing ...

if(oWebPartPage.Web.IsRootWeb
   oWebPartPage.Dispose();

Conclusion
Many SharePoint objects implement the IDisposableinterface, so you must take care when using these SharePoint objects to avoid retaining them in memory. By following the guidelines for their disposal, as described in this article, you can help to ensure reliability of your Windows SharePoint Services customizations.

 

 

 

 

 

 

 

 


posted on 2010-06-03 14:46  chanderler  阅读(251)  评论(0编辑  收藏  举报