英文原文: 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的对象的工作必须处理的对象的应用程序结束时使用它们。您不应该依赖于垃圾收集器会自动从内存中释放他们。
寻找对象的不正确处理完毕
你可以通过问以下问题的正确处理可能存在的对象:
- 您的应用程序池回收频繁,特别是根据(假设该应用程序池设置回收内存时达到阈值)重物?
内存大小应该800 MB- 1.5 GB的(假设至少2 GB的RAM)。设置回收应用程序池越接近1 GB的发生提供了最好的结果,但实验,以确定哪些设置适合您的环境。如果回收设置太低,你的经验,因为频繁的回收应用程序池的性能问题。如果设置得太高,你的系统遇到性能问题,因为页交换,内存碎片,以及其他事项。 - 请问您的系统表现不佳,尤其是在负载很重的情况?随着内存使用量开始增加,系统必须赔偿,例如,通过寻呼内存和处理内存碎片。
- 请问您的系统崩溃或用户遇到意外的错误,特别是在重负载,如超时或页面没有可用的错误意外的错误?同样,当内存使用量的增加或获取支离破碎,有些功能不能失败,因为他们对其他操作分配内存。在许多情况下,代码不正确处理“内存不足“异常,从而导致虚假或误导性的错误。
- 请问您的系统使用自定义或第三方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 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 对象模型时的常见代码问题 (来源MSDN)
在sharepoint中 使用SPSiteDataQuery来进行跨列表查询
浙公网安备 33010602011771号