JOJ
踏踏实实做人,认认真真做事!放纵自己就是毁灭自己!

英文原文: 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的对象的工作必须处理的对象的应用程序结束时使用它们。您不应该依赖于垃圾收集器会自动从内存中释放他们。

寻找对象的不正确处理完毕

你可以通过问以下问题的正确处理可能存在的对象:

  1. 您的应用程序池回收频繁,特别是根据(假设该应用程序池设置回收内存时达到阈值)重物?
    内存大小应该800 MB- 1.5 GB的(假设至少2 GB的RAM)。设置回收应用程序池越接近1 GB的发生提供了最好的结果,但实验,以确定哪些设置适合您的环境。如果回收设置太低,你的经验,因为频繁的回收应用程序池的性能问题。如果设置得太高,你的系统遇到性能问题,因为页交换,内存碎片,以及其他事项。
  2. 请问您的系统表现不佳,尤其是在负载很重的情况?随着内存使用量开始增加,系统必须赔偿,例如,通过寻呼内存和处理内存碎片。
  3. 请问您的系统崩溃或用户遇到意外的错误,特别是在重负载,如超时或页面没有可用的错误意外的错误?同样,当内存使用量的增加或获取支离破碎,有些功能不能失败,因为他们对其他操作分配内存。在许多情况下,代码不正确处理“内存不足“异常,从而导致虚假或误导性的错误。
  4. 请问您的系统使用自定义或第三方Web部件或自定义应用程序?您可能没有意识到,他们必须处理的SharePoint对象和原因,假定垃圾收集自动执行此功能。然而,这并不是真正的所有情况。

如果你回答是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对象是垃圾收集。

第一种情况发生最频繁,特别是如果您的网站使用了八个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 method
  • using clause
  • try, catch, and finally blocks
  • 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块。

    The try, catch, and finally Blocks

    使用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,将自动释放资源
     }

     

    Response.Redirect with try, catch, and finally Blocks and using Statements

    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 Objects

    本节介绍中,新的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", 
          "roger.lamb@litwareinc.com");
        // 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", 
          "roger.lamb@litwareinc.com"))
        {
        } // 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对象模型性能考量 (推荐阅读)

    使用 SharePoint 对象模型时的常见代码问题 (来源MSDN)

    在sharepoint中 使用SPSiteDataQuery来进行跨列表查询

    不要忘记SPUtility类

     

    Technorati 标签: sharepoint,moss,Disposable,wss
    posted on 2011-03-18 22:55  JoinJ  阅读(398)  评论(0编辑  收藏  举报