SharePoint 2013 应用开发实战
上QQ阅读APP看书,第一时间看更新

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属性获取网站集合中各个网站的信息。