英文原文: http://msdn.microsoft.com/zh-cn/library/aa973248.aspx
这里中文主要使用Google Translate, 如果你英语更好建议看原文! 实在不行可以看code,我就是这样Google Translate +code! 大概还是懂起文章的意思了!
主要讲的是我们在使用sharepoint对象模型的时候,是否及时释放资源,好的代码和坏的代码风格比较! 不保证翻译很准确,请大家指正!
介绍使用一次性的Windows SharePoint Services对象
在Windows SharePoint Services 3.0对象模型中的对象作为界面与Windows SharePoint Services数据的工作。通常,开发人员调入对象模型来读取数据或写入新的数据存储在Windows SharePoint服务。
Windows SharePoint Services对象模型包含对象实现了IDisposable接口。你必须采取预防措施时,使用这些对象,以避免他们长期在Microsoft .NET Framework的内存中!
具体来说,你应该明确地处置这些SharePoint对象实现IDisposable接口你完成的时候使用它们。
您使用SharePoint对象广泛,例如,在SharePoint网站使用自定义Web部件,您可能会导致通过SharePoint对象不处理以下异常行为时,你已经完成了他们。
1.频繁回收的Windows SharePoint Services应用程序池,尤其是在用电高峰
2.应用程序崩溃,由于堆在调试器中出现的腐败
3.高级Microsoft Internet信息服务(IIS)的工作进程内存使用
4.糟糕的系统和应用性能
本文作为一个以处理和SharePoint对象实现IDispose处理的正确程序指南。在本文所讨论的问题也是由SharePoint处置标记检查器工具,免费下载该方案作为检查编码做法的原因,因为处理不当和SharePoint对象处理内存泄漏您的程序集可用。
几个Windows SharePoint Services对象,主要是SPSite类和SPWeb类的对象,几个创建为管理对象。然而,这些对象使用非托管代码和内存来执行他们的大部分工作。对象的管理部分是比非托管部分较小。因为较小的管理部分不放在垃圾收集器内存压力,垃圾回收器不会及时释放从内存中的对象。该对象的非托管内存的大量使用会导致不寻常前面描述的一些行为。调用应用程序在Windows SharePoint Services的IDisposable的对象的工作必须处理的对象的应用程序结束时使用它们。您不应该依赖于垃圾收集器会自动从内存中释放他们。
寻找对象的不正确处理完毕
你可以通过问以下问题的正确处理可能存在的对象:
如果你回答是4个Yes,以及一个或更多的其他问题,有一个很好的机会,你的自定义代码不正确处理的项目。
如果您的网站是展示不同寻常的行为之一如前所述,你可以判断一个内存泄漏的原因是由于通过检查对象的诊断日志(在C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\LOGS)有关SPRequest对象。每个实例包含的SPSite和SPWeb一个对SPRequest对象,反过来,包含一个引用到一个非托管COM对象,处理与数据库服务器通信的参考。 Windows SharePoint Services的监察SPRequest对象,在每一个特定的线程,在线程并行存在的数量,并增加了有用的条目下的三种情况记录:
第一种情况发生最频繁,特别是如果您的网站使用了八个SPRequest对象的默认阈值。每当SPRequest对象的数量超过此阈值时,下列项目中出现的诊断日志:
潜在的SPRequest对象人数过多(数目的对象)目前尚未发布有关线程的线程数。确保该对象或其母公司(如SPWeb或SPSite对象)正在妥善处理。为此对象分配标识{GUID},
最佳阈值根据不同的性质,您的网站上运行的应用程序。当您的网站正遇到性能问题,您应该监控您的安装的地下物流系统日志,了解许多物件SPRequest网站正在创建的应用程序。这可以帮助您确定您的网站和应用程序的设计是创建过多SPRequest对象。即使对象不正确的处理是不是你的表现问题的原因,您可能需要重新设计你的网站或定制网站应用程序,以降低整体记忆体消耗的SPRequest对象过度增殖造成的。
因为默认的门槛很低,可能并不适用于许多网站,你可以改变这种通过编辑以下注册表子项的门槛:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings
LocalSPRequestWarnCount = 所需的阈值
在您确定的对象不正确的处置可能造成SPRequest对象增殖,不必要地增加了内存占用您的网站,您可以通过以下二项前瞻性的不正确处置的具体实例。两个消息,以至于内存被因为不正确的SharePoint对象出售浪费案件,都涉及到的数量和SPRequest对象的状态在一个线程:
… 跳过一点, 直接看代码处理这段, 还是看代码情切点…
编码技术来确保对象的处置
你也可以使用某些编码技术,以确保对象的处理。这些技术包括使用在您的代码如下:
Dispose VS . Close方法处理对比
为SPWeb对象和功能相同的方式SPSite对象的Dispose和Close方法。 Dispose方法调用对象的Close方法。我们建议调用Dispose方法,而不是关闭,因为SPWeb和的SPSite对象实现了IDisposable接口,和标准的。NET Framework垃圾收集调用Dispose方法释放从内存与对象关联的所有资源。
//不要这样做。SPWeb自动调用Dispose() using( SPWeb web = SPControl.GetContextWeb(HttpContext.Current)) { ... } //SPContext对象是由SharePoint管理框架,不应在代码中明确处置。这是真实的也由SPContext.Site,SPContext.Current.Site,SPContext.Web和SPContext.Current.Web返回的SPSite和SPWeb对象。
//在下面的代码示例中,一个SPSite对象被实例化,但不处理,因为运行时可确保只有OpenWeb()返回SPWeb对象 void CombiningCallsLeak() { using (SPWeb web = new SPSite(SPContext.Current.Web.Url).OpenWeb()) { // ... New SPSite will be leaked. 在new spsite 将溢出,下面嵌套using } // SPWeb object web.Dispose() automatically called. } //您可以通过嵌套using在另一份声明中使用解决此一问题。 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. } //如果您不是SPSite对象上执行任何操作,你可以写这个更简洁,如下面的代码示例。 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. }
在其他情况下,必须建立自己的尝试,catch和finally块。最明显的例子是情况下,您需要处理的异常,因此必须包含一个catch块。以下部分提供了有关何时以及如何使用try,捕捉方针,finally块。
使用try,catch和finally块,显然是有道理的,当您需要处理异常。代码中的任何一个try / catch块应该有一个理事最后条款,确保实现IDisposable对象处置。请注意,在下面的代码示例中,您应该填写的代码,处理异常的catch块。切勿空的catch块。另外,请注意为空测试前处理的最佳做法。
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和finally语句块或使用须避免潜在的漏洞,当您建立一个foreach块内可支配的对象,如下面的代码示例所示。
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. 这里使用using,将自动释放资源 }
finally块执行在try块后调用Response.Redirect。 Response.Redirect最终生成一个ThreadAbortException。引发此异常时,运行时执行的所有线程结束前终于块。但是,因为finally块可以做一个无限的计算或取消ThreadAbortException,也不能保证该线程将结束。因此,在任何重定向或加工转移可以发生,你必须处理的对象。如果您的代码必须重定向,实施的方式类似于下面的代码例子。
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(); } //因为using每次运行生成一个finally块,每当你使用Response.Redirect在Using里,确保妥善处置对象。下面的代码示例显示了如何做到这一点。 using (SPSite oSPSite = new SPSite("http://server")) using (SPWeb oSPWeb = oSPSite.OpenWeb(..)) { if (bDoRedirection) Response.Redirect("newpage.aspx"); }
//良好的编码习惯#1 明确调用Dispose() void CreatingSPSiteExplicitDisposeNoLeak() { SPSite siteCollection = null; try { siteCollection = new SPSite("http://moss"); } finally { if (siteCollection != null) siteCollection.Dispose(); } } //良好的编码习惯#2 自动调用Dispose() CreatingSPSiteWithAutomaticDisposeNoLeak() { using (SPSite siteCollection = new SPSite("http://moss")) { } // SPSite object siteCollection.Dispose() is called automatically. } //好的编码习惯 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. }
不共享任何SPRequest对象(和扩展名的任何对象,它包含一个对SPRequest对象的引用)跨线程。任何编码技术,股份两个或多个线程之间的SPRequest对象,或者创建一个线程上一SPRequest对象,它在另一个处置,不支持。这意味着你可以不存储任何对象拥有一个在静态变量SPRequest对象的引用。不这样做,因此,商店的SharePoint对象实现IDisposable的静态变量(如SPWeb或的SPSite)。
本节介绍中,新的SPSite对象返回,必须加以处置的情况。在一般情况下,任何时间调用应用程序使用SPSite构造器,它应该调用Dispose方法时,使用完对象。如果SPSite对象是从GetContextSite获得,调用应用程序不应处理的对象。由于SPWeb的SPSite对象和保持一个内部列表是根据这种方式,对象可能会导致SharePoint对象模型的行为不可预测的处置。在内部,Windows SharePoint Services中列举了这个清单完成后,页面的对象妥善处理。
//不好的编码做法 void SPSiteCollectionAddLeak() { SPWebApplication webApp = new SPSite("http://moss").WebApplication; SPSiteCollection siteCollections = webApp.Sites; SPSite siteCollection = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\\User", "[email protected]"); // SPSite siteCollection leak. 没有释放 } //良好的编码做法 void SPSiteCollectionAddNoLeak() { SPWebApplication webApp = new SPSite("http://moss").WebApplication; SPSiteCollection siteCollections = webApp.Sites; using (SPSite siteCollection = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\\User", "[email protected]")) { } // SPSite object siteCollection.Dispose() automatically called. }
/** * SPSiteCollection [ ] Index Operator *该SPSiteCollection[]索引操作符返回每个访问新的SPSite对象。一个实例是创建的SPSite对象,即使是已经访问。下面的代码示例演示SPSite对象正确的处理。 */ //Bad Coding Practice #1 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 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 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 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) * 本节描述了AllWebs属性集合,要求SPWeb对象访问后要关闭的方法,属性或其他操作。 * 该SPSite.AllWebs.Add方法创建并返回一个SPWeb对象。你应该从SPSite.AllWebs.Add处置返回的任何SPWeb对象。 **/ //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 * 该SPWebCollection.Add方法创建并返回一个SPWeb对象,需要加以处置。 * **/ //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 * 该SPSite.AllWebs[]索引操作符返回一个新的SPWeb实例在每次访问时间。创建一个对象的索引操作过程中,即使是已经访问该对象。如果没有正确关闭,下面的代码示例中的一个离开的.NET Framework垃圾回收器SPWeb对象。 * **/ //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 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 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 * 该OpenWeb方法和SelfServiceCreateSite方法(所有签名)创建一个SPWeb对象并返回给调用者。这个新的对象不是存储在SPSite对象, * 也不是任何地方弃置在SPSite类。基于这个原因,你应该通过这些方法处理创建的任何对象。 * **/ //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. }
还有很多很多类似的,记得使using哦,太多了,不想复制过来了, 待以后在整理上来吧,今天就这么多!
还有几篇关于sharepoint的文章,推荐阅读:
使用 SharePoint 对象模型时的常见代码问题 (来源MSDN)
在sharepoint中 使用SPSiteDataQuery来进行跨列表查询