现在的位置: 首页 > 综合 > 正文

在WSS3.0和MOSS2007中排查SPSite或SPWeb的泄露问题

2013年05月07日 ⁄ 综合 ⁄ 共 5796字 ⁄ 字号 评论关闭

之前的文章中, 我已经讨论过: SPSite和SPWeb(还有潜在的PublishingWeb)对象需要被恰当地析构, 来避免内存不足的问题. Roger Lamb有一个非常好的代码模式的总结, 会引发这样的泄露, 并且提供了这样的代码应该如何调整才能避免这样的问题发生.

 

在这篇文章中, 我会提供给你一些关于SharePoint管理员如何能够定位站点中自定义代码没有恰当析构这些对象的情况, 还有如何定位泄露这些对象的组件.

 

总论

===========

正如之前的文章所说的, SPWeb和SPSite对象保有一份SPRequest对象的引用, SPRequest对象会保有一个SharePoint COM组件的引用, 这个COM组件会负责与后台的SQL Server通信. 对SPWeb和SPSite的析构是必要的, 这样可以保证SPRequest对象会释放掉COM对象还有所有由这个COM对象分配的内存也会被一同释放.

 

在SharePointV2中, 鉴别这种对象泄露的唯一方式就是分析内存转储文件- 通常需要给微软开启一个技术支持事件, 来分析没有被正确析构的SPWeb或SPSite对象.

 

SharePoint v3就不一样了, 它包含了能够监控对SPRequest对象正确析构的代码(所以, 就会监控SPWeb和SPSite对象的正确析构). 这份代码有两个不同的特性:

  1. 它监控在所有的并行的线程中有多少个SPRequest对象被打开.
  2. 它检查在创建SPRequest的线程结束的时候SPRequest对象是否还存在.

我将要讨论这些特性能如何被用来检测你站点设计的代价, 还有代码中是否包含任何没有正确析构SPWeb和SPSite的控件.

 

一个对我站点的请求会分配多少个SPRequest对象?

============

通常情况下, 你会认为当你没有向页面添加任何代码的时候, 那就只需要一个SPSite和SPWeb对象, 但这是不正确的. 原因是所有的master page都包含有三个导航控件:

  • Top navigation
  • Breadcrumb control
  • Left Navigation control

每一个这些默认的控件都会与一个与SharePoint一同release的Site Map Provider绑定到一起. 为了在导航控件中显示站点元素, 这些控件会查询site map provider, 而site map provider回去遍历在sharepoint数据库中的站点来取回应该在导航控件中显示的title和URL. 每一个被遍历的站点都会在为了取回他们的属性而启动的遍历过程中被初始化. 这意味着, 如果你有一个导航控件, 该导航控件被设置为遍历三层的站点, 而每一层中有20个站点, 那么最终你将会实例化400个SPRequest对象. 所以, 你需要非常小心地配置你的导航控件, 还有每层次中有多少个站点!

 

现在应该问的一个问题是: 为什么site map provider没有在拿到URL和title之后直接地析构任何的SPRequest对象? 这会大规模地减少同时存在的SPRequest对象的数目.

确实, 这是正确的, 但是请记住: 你通常在一个页面中会有多个导航控件! 读取了prpperty之后析构掉这些对象意味着对于每一个在页面上的导航控件, 同样的对象将要一次又一次地从数据库读取- 这会影响到站点的性能. 为了避免这些item重复地被导航控件读取, 他们被缓存起来了, 保证其他的需要相同items的导航控件可以使用这些缓存起来的item, 而不会引发额外的对后台数据库的请求. 需要注意的是, 站点的总体内存消耗会因为这种实现方式而上升.

 

如同在overview部分阐述过的, sharepoint v3会监控在相同的线程里有多少个SPRequest对象并行地存在. 一旦Sharepoint鉴别出了一个新的SPRequest的分配导致这个对象的数目超过了可配置的阀值的话, SharePoint会在ULS日志中创建一个如下的条目:

 

12/12/2007 12:29:54.05 w3wp.exe (0x1380)                       0x126C  Windows SharePoint Services   General                                0                Medium               Potentially excessive number of SPRequest objects (14) currently unreleased on thread 5.  Ensure that this object or its parent (such as an SPWeb or SPSite) is being properly disposed.  Allocation Id for this object: {2DF92EAD-4959-4A21-8BA0-FBD91E8FDD48} Stack trace of current allocation: at Microsoft.SharePoint.SPRequestManager.Add(SPRequest request, Boolean shareable)

 

得到警告的默认的阀值是8- 实际上比实际情形下的值小很多, 我们刚才就已经讨论过, master page中包含有导航控件, 那肯定会超过这个值. 正如你在上面的ULS的简单例子中看到的,14个SPRequest对象并存在相同的线程里. 因为我们之前已经见过导航控件中有大量的SPRequest对象时绝对正常的, 也是符合预期的, 所以, 你可以预计到ULS日志中会被填满像这样的警告.

 

你应该检查你站点的ULS日志, 验证在这些警告里的最大值是多少, 来获得你当前站点设计中由于分配SPRequest对象引起内存消耗代价的一点概念. 如果这个数字超过了150, 你也许需要考虑一下修改你站点的设计或者在你导航控件中显示的站点深度, 以便减轻内存消耗的足迹.

 

在这之后, 你可以慢慢调整阀值来避免不必要的警告. 调整的方法是通过修改下面的注册表键值(比如设置为50):

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings

        LocalSPRequestWarnCount = 50

 

实际情况下, 这里应该配置的数值取决于你的站点结构. 在设置了这个注册表键值之后, 你需要重启SharePoint的各项服务还有Application Pool(或者干脆重启服务器)来确保这个修改能够生效.

 

还有, 请注意你只能在配置了诊断日志记录中的General这个category至少是"中等", 你才能看到上面的警告. 中等是默认的等级.

 

在我的站点中有没有那种没能正确析构SPSite/SPWeb的代码呢?

==================

一种方法(不完全可靠, 但却是一种很好的指示器)是监控ULS日志信息中的上一部分的信息. 如果有些线程在它的生命期中这个数字变得越来越大, 从来不往下降到阀值一下, 那么出现没有正常析构SPSite/SPWeb对象的代码的概率就是很高的了.

 

注意你不能只看列在这些信息中的调用栈来鉴别问题代码, 因为这个警告是在分配对象的时候出现的. 这个信息并不会指示出没有被正常析构的对象! 所以, 你只能把它用作一个指示器, 用来分析有些代码可能有错, 但是并不能甄别出引发问题的模块.

 

为了甄别出坏代码, 我们需要一个不同的方式.

 

就像在overview部分提到的, SharePoint不仅仅监控有每一个线程分配的SPRequest对象的数目, 还会监控在每个线程结束的时候, SPRequest对象是否还存在, 还会创建如下的信息在ULS日志中:

 

05/01/2007 12:58:47.31                w3wp.exe (0x105C)                                      0x09A8 Windows SharePoint Services                   General                               8l1n       High       An SPRequest object was not disposed before the end of this thread.  To avoid wasting system resources, dispose of this object or its parent (such as an SPSite or SPWeb) as soon as you are done using it.  This object will now be disposed.  Allocation Id: {5BFFCA4B-3B91-45BF-98CD-0BB508BE30EE} To determine where this object was allocated, create a registry key at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings. Then create a new DWORD named SPRequestStackTrace with the value 1 under this key.

 

注意, 线程的生命期可以远比一个single request要长. 因为sharepoint仅检查是否有没被析构的SPRequest在线程结束的时候还存在, 而不是在每一个请求结束的时候进行检查, 这种类型的entries会比你预期中的要小很多. 所以, 你会需要在多个ULS日志中搜索这个警告, 而不仅仅是查看最近的一个ULS日志(实际上, 你可以搜索字符串8l1n, 这是这条消息的唯一标示).

 

另外, 如果这个对象被垃圾收集器析构的话, 你将会看到一条稍微不同的消息, 说明垃圾收集器回收了这个对象:

 

05/01/2007 14:48:43.32                w3wp.exe (0x105C)                                      0x09A8 Windows SharePoint Services                   General                               8l1n       High       An SPRequest object was reclaimed by the garbage collector instead of being explicitly freed. To avoid wasting system resources, dispose of this object or its parent (such as an SPSite or SPWeb) as soon as you are done using it.  Allocation Id: {098092FF-E4D9-4BA3-B2B8-5C6CE9B96554}  To determine where this object was allocated, create a registry key at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings.  Then create a new DWORD named SPRequestStackTrace with the value 1 under this key. 

 

To identify which code is responsible for the problem after you identified the problem you have two options:

为了鉴别哪段代码要为这个错误负责, 你有两个选择:

  1. 你可以在更早的ULS日志中搜索警告中出现的allocation ID, 如果分配是在超过阀值之后完成的, 你是能够找到它的.

  2. 你可以遵循警告中的提示, 配置下面的注册表键值, 这会确保内存中SPRequest对象分配的调用栈被保存下来, 并且在ULS日志中添加8l1n信息记录.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings

        SPRequestStackTrace = 1

 

第二个选项更加可靠因为它确保你能为每一个8l1n信息获得调用栈. 注意, 你配置这个注册表键之后, 你需要重启sharepoint的服务还有application pool, 来确保键值生效.

 

通过分析ULS给出的调用栈, 你就能够定位引发问题的组件了.

 

2011年3月18更新

=================

微软公布了一个工具, 专门用于检测已经build好了的DLL中的SPWeb, SPSite, PublishingWeb等对象的dispose问题.

这个工具可以在这里得到.

SharePoint Dispose Checker Tool

http://archive.msdn.microsoft.com/SPDisposeCheck

 

笔者亲自用过这个工具, 真刀真枪地解决过不少问题. 其中之一就是sharepoint上的应用间歇性地连不上数据库, 原因就是spweb有泄露.

强烈推荐这个工具, 非常好用!

 

详细的代码实践可以在这里

Best Practices: Using Disposable Windows SharePoint Services Objects

http://msdn.microsoft.com/en-us/library/aa973248%28v=office.12%29.aspx 

 

译自:

Troubleshooting SPSite/SPWeb leaks in WSS v3 and MOSS 2007

http://blogs.technet.com/stefan_gossner/archive/2008/05/07/troubleshooting-spsite-spweb-leaks-in-wss-v3-and-moss-2007.aspx

抱歉!评论已关闭.