2.3 对象的释放
上一节介绍的SPSite和SPWeb对象是开发过程中应用服务器端对象模型操作SharePoint的基础和入口,几乎每一次都会用到它们。这两个对象实现了IDisposable接口(如果对象具有Dispose方法,则意味着实现了IDisposable接口),使用它们时需要注意及时释放,以防止不再使用的对象堆积在内存而造成异常情况。
这些异常情况包括:
● SharePoint应用程序池频繁地进行回收,尤其在使用高峰期。
● 应用程序崩溃。
● IIS工作进程W3WP占用的内存异常大。
● 系统和应用程序性能低下。
明确地说,在使用完诸如SPSite、SPWeb这样的对象后,要及时显示地释放她们,而不应依赖垃圾回收器。
2.3.1 如何应用Dispose
如何判定发生了此类异常呢?可以检查ULS日志(位于C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\LOGS目录),看看其中是否有与SPRequest对象相关的条目。SharePoint会监视每个特定线程和并行线程中存在的SPRequest对象的数目,并在以下三种情况下向日志中添加有用的条目:
(1)SPRequest对象的总数超出了可配置的阈值。
(2)SPRequest对象在线程结束后继续存在。
(3)SPRequest对象已通过垃圾收集从堆中移除。
第一种情况是最常发生的,每当SPRequest对象的数目超过网站的阈值时,ULS日志中就会出现以下条目:
● “可能有过量的SPRequest对象(对象数)当前在线程线程号上未得到释放。请确保正确释放此对象或其父项(如SPWeb或SPSite对象)。此对象的分配ID为:{GUID}”
如果想增大网站的阈值,可以通过编辑以下注册表子项来更改此阈值:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings
LocalSPRequestWarnCount = 所需阈值
在确定可能有该内存问题时,可以通过查找以下两条消息来确定错误释放的特定实例:
● 在此线程结束之前未释放SPRequest对象。为了避免浪费系统资源,请在用完此对象或其父项(如SPSite或SPWeb)之后立即将其释放。现在将释放此对象。分配ID: {GUID}。要确定在哪里分配了此对象,请在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings处创建注册表子项。然后在此项下面创建名为SPRequestStackTrace且值为1的新DWORD。
此消息表明SPRequest对象由于在线程结束后仍然存在而被释放。
● SPRequest对象已被垃圾收集器回收,而不是被明确释放。为了避免浪费系统资源,请在用完此对象或其父项(如SPSite或SPWeb)之后立即将其释放。分配ID: {GUID}。要确定在哪里分配了此对象,请在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings处创建注册表项。然后在此项下面创建名为SPRequestStackTrace且值为1的新DWORD。
此消息表明垃圾收集器释放了SPRequest对象。
确保释放的编码方式有三种:显示调用Dispose方法、使用using语句块和使用try、catch和finally语句块。
显示调用Dispose方法:web.Dispose();
使用using语句块:
using (SPSite site = new SPSite("http://mysiteurl/subsite")) { using (SPWeb web = site.OpenWeb()) { //todo } }
使用try、catch和finally语句块:
SPSite site = null; SPWeb web = null; try { site = new SPSite("http://server"); web = site.OpenWeb(); } catch (Exception e) { //处理发生的异常 } finally { if (web != null) web.Dispose(); if (site != null) site.Dispose(); }
另一方面,对于从上下文(即SPContext)中获取到的对象是不能进行释放的,这个会由SharePoint框架自行管理,如果主动释放会造成异常抛出。这些对象包括:SPContext.Site、SPContext.Current.Site、SPContext.Web和SPContext.Current.Web等。
2.3.2 部分编码最佳实践
开发人员应该从平时多多积累最佳实践的经验,避免写出有潜在威胁的代码。
如果读者有过一些SharePoint的维护或开发经验,就会切身感受到有些SharePoint的异常不是特别好处理,一个问题可能有几种不同的原因导致。因此对于这种可以明确规避的风险,请务必要认真对待,将其克服掉。
下面是关于开发过程中SPSite和SPWeb对象释放方面最佳实践的一些说明。
● SPSiteCollection.Add方法将创建并返回新的SPSite实例,需要释放。
● SPSiteCollection[]索引运算符会针对每次访问返回一个新的SPSite实例,需要释放。
● SPSite.AllWebs.Add方法将创建并返回SPWeb实例,需要释放。
● SPWebCollection.Add方法将创建并返回SPWeb实例,需要释放。
● SPSite.AllWebs[]索引运算符会在每次访问对象后返回一个新的SPWeb实例,需要释放。
● SPSite对象的OpenWeb方法和SelfServiceCreateSite方法将创建SPWeb对象并将其返回给调用方,需要释放。
● Microsoft.Office.Server.UserProfiles.PersonalSite返回的SPSite对象,需要释放。
● SPWeb.Webs属性将返回SPWebCollection对象。需要释放此集合中的SPWeb对象。
● SPWeb.Webs.Add方法(或SPWebCollection.Add)会创建并返回新的SPWeb对象,需要释放。
● SPWeb.Webs[]索引运算符会针对每次访问返回一个新的SPWeb对象,将会通过调用OpenWeb方法来创建SPWeb,需要释放。
● 如果开发中仅需要获取网站的信息,可以避免构建SPWeb对象而是使用SPWebCollection的WebsInfo属性获取网站集合中各个网站的信息。