Robin's Blog

记录 积累 学习 成长

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
代码下载位置: Bugslayer2007_11.exe (209 KB)
Browse the Code Online
Microsoft® .NET Framework 怎么会被视为无需考虑内存的环境?这真是太有趣了!当然,现今依然困扰托管应用程序的最首要问题之一是什么呢?是内存!为什么?当垃圾收集器运行以回收内 存时,公共语言运行时 (CLR) 会挂起应用程序中的所有线程,因此不会完成任何工作。当无法完成任何工作时,即表明存在性能问题。
无 论您如何关注您的 .NET Framework 内存使用情况,在获取有用的内存情况时,实际上有几个主要的问题会产生干扰。首先,尽管 .NET Framework 从您的利益出发进行了大量的工作,但是要查明当前正在执行哪些工作以及使用了多少内存,文档未必会提供帮助。幸运的是,Lutz Roeder 的 Reflector for .NET (aisto.com/roeder/dotnet) 能够起很大的帮助作用。
观察内存使用情况的第二个问题是,作为 .NET 主要开发工具的 Visual Studio® 不会显示有关内存的任何情况。这并不是 Visual Studio 团队的过错,而是原始 CLR 体系结构中的一个错误,因为它没有通过 CLR 调试 API 公开一种查看特定对象处于哪个世代的方式。虽然可以使用 CLR 分析 API 跟踪对象世代,但是这会耗费大量的精力,而且在调试时无法产生作用。正如我在 2003 年 6 月刊 (msdn.microsoft.com/msdnmag/issues/03/06/Bugslayer) 并再次在 2005 年 3 月刊 (msdn.microsoft.com/msdnmag/issues/05/03/Bugslayer) 的 Bugslayer 专栏中所述,您可以使用 Son of Strike (SOS) WinDBG 扩展来查看需要的所有内存信息,但是它可能会是自首个 Windows® 95 内核调试器面世以来最令人痛苦的一个工具。
在 此次的 Bugslayer 专栏中,我要展示一种工具,通过它可以看到 ASP.NET 应用程序中最隐匿的性能杀手之一:视图状态。您将会看到,能够轻松地在生产服务器上查看您的视图状态使用情况可能意味着这样的差异:花 30 分钟进行调试和花 6 周时间进行猜测。

视图状态
在 ASP.NET 页面中,视图状态是一种极其优秀的机制,通过该机制,页面上的控件状态被存储在发送给客户端的 HTML 代码中的隐藏值中。这就避免了在 ASP.NET 工作进程中维护页面状态的任务,而这将会对服务器产生更大的内存压力。如果您曾经在 Internet 的侏罗纪时期(1996 年前后)使用过用本机 C++ 中编写的 ISAPI 筛选器,就会记得跟踪页面、会话和应用程序状态需要付出多大的努力。如果您没有太多的 ASP.NET 经验,可以将 Dino Esposito 在 2003 年 2 月刊中介绍视图状态的“领先技术”专栏作为入门读物来阅读 (msdn.microsoft.com/msdnmag/issues/03/02/cuttingedge)。
视 图状态的问题在于确实无法弄清楚在某个给定时间视图状态值有多大。当页面上有 GridView 控件时,情况尤其如此。在页面上获取该数据库信息的这种易用性需要付出一定的代价。好消息是,与 ASP.NET 1.1 相比,Microsoft 开发人员已经在 ASP.NET 2.0 中竭尽全力来减小视图状态的大小。然而,在过去几个月内,在我帮忙调试的几个大型 ASP.NET 2.0 应用程序中,其性能问题均由某些页面上特大尺寸的视图状态引起。

调试提示
在介绍工具和代码之前,我想讨论一些调试技巧。当开发人员就某个出现性能问题的 ASP.NET 应用程序打电话咨询我时,他们总是说:“这个应用程序本来运行得好好的,但现在实在是太慢了。”
我的第一个问题是:“您是否在应用程序中使用了任何 GridView?”因为大多数 ASP.NET 应用程序是数据库的前端,这几乎是一个定式,但并不总是这样。如果他们没有使用 GridView,我需要另辟蹊径。
我 要问的第二个问题是:“我可以看一下您在版本控制系统中的数据库存储过程历史记录吗?”遗憾的是,对于这个问题,很多开发团队都一下子被难住了,然后告诉 我在版本控制系统中没有他们的存储过程。上次我看到的一个 SQL 存储过程是代码,如果您遵循开发的常识规则,所有代码都应该加入版本控制系统中。实际上,Microsoft 通过 Visual Studio 2005 Team Edition for Database Professionals 使这一过程变得极其简单。如果在版本控制系统中没有您的存储过程,请立即停止阅读本文,并添加它们。
我之所以要求查看版本控制系统中的存储过程,是因为如果应用程序运行良好,然后突然开始出现问题,那么就需要找出以前版本和当前版本之间的变化。我说不清有多少次在浏览存储过程的版本控制更改日志时发现有人已将其中的某个查询更改为类似于以下的代码行:
   select * from table
当您将该输出抽取到 GridView 控件(严重依赖于视图状态)且数据库包含六百万个记录时,便会产生严重的视图状态问题!
尽 管视图状态易于使用,但当出现问题时,将几乎无法查看视图状态。大多数开发人员知道,通过打开 ASP.NET 跟踪即可查看页面的视图状态。遗憾的是,ASP.NET 跟踪仅存储有限数量的跟踪,而且它在生产服务器上的速度可能会极其慢。另一个方法是让每个用户右键单击浏览器,从上下文菜单中选择“查看源文件”,然后开 始手动计算字符数。很明显,我们需要更好的方法:即一种为生产服务器上的所有页面跟踪最大视图状态的简便方法,这样您的数据便可存放在一个地方,以方便做 出明智的调试决定。

目标
当 我第一次开始考虑可让您轻易看到视图状态使用情况的工具所面临的挑战时,首要目标就是无论我最终拿出什么,都必须可以在生产环境中使用。这意味着代码必须 既快速又节约内存。因此,我决定只为每个特定的页面存储最大视图状态大小。如果我存储每个页面的所有视图状态大小,那么很可能就必须要存储数百万个记录。 通过仅存储给定页面的最大视图状态,我可以极大地降低存储要求,从而确保 ASP.NET 应用程序中的数据项目绝对不会超过 .aspx 文件的数量。
我还想简化查看视图状态数据的过程,使其易于理解,甚至对非开发人员也是如此。尽管查看页面的总视图状态大小很棒,但是我增加了一个额外的要求,即存储和显示页面上所有控件的视图状态大小的选项。这会使得找到最有助于解决问题的控件更容易。
同时,我还希望能够使用可查看 HTTP GET 请求的视图状态大小的选项。当几乎所有的视图状态问题都与 HTTP POST 请求相关时,用户可以存储特定页面的完整 URL 并直接转到该页。虽然这种情况很少见,但是为了完整性起见,我希望包括此选项。
最后,我想要确保该工具的轻松部署。作为一个附属目标,如果我的解决方案无需更改源代码,那就非常理想。
本 月专栏可供下载的源代码包括了内含所有魔力的程序集:Bugslayer.Web。此外,还包含完整的单元测试,以便您可以试用该程序集并查看它如何运 行。对于生产服务器,需要将 Bugslayer.Web.DLL 安装到全局程序集缓存 (GAC) 中,因为这样便可从计算机的任何位置加载代码。
Bugslayer.Web 包含两个部分:HttpModule 与 HttpHandler。如果您已阅读了许多有关 ASP.NET 处理管道的知识,您大概可以猜测到,HttpModule 是数据收集发生地,而 HttpHandler 则是用来显示数据的。这意味着如果要使用 Bugslayer.Web,您根本无需更改代码。
要将 ASP.NET 应用程序配置为使用 HttpModule,需要将与如下类似的 <httpModules> 元素添加到 web.config 文件:
<?xml version="1.0"?>
<configuration>
<system.web>
<httpModules>
<add name="ViewStateStats"
type="Bugslayer.Web.ViewStateStatisticsModule"/>
</httpModules>
</system.web>
</configuration>
设置 HttpHandler 也同样简单。按如下方式在 web.config 文件中添加 <httpHandlers> 元素:
<?xml version="1.0"?>
<configuration>
<system.web>
<httpHandlers>
<add verb="*"
path="viewstatestats.axd"
type="Bugslayer.Web.ViewStateStatisticsHandler"
validate="false" />
</httpHandlers>
</system.web>
</configuration>
请 注意,Bugslayer.Web.ViewStateStatisticsHandler 会映射到 viewstatestats.axd。我选择使用 .axd 扩展名,因为 IIS 已将它识别为 ASP.NET 的扩展名类型。通过将路径属性设置为特定的文件名,ASP.NET 将会调用该特定的 HttpHandler,但是对其他文件名的请求则会路由到 ASP.NET 内部 .axd 处理程序。

跟踪视图状态
在安装了 Bugslayer.Web 并将条目添加到 web.config 之后,您将会记录每个页面的最大视图状态的字节数。虽然能够收集数据这一点很好,但您需要能够查看它,这就是 ViewStateStats.axd 发挥作用之处(如前所述)。图 1 显示输出示例。格式看起来可能会有点熟悉,因为其中的样式是从 ASP.NET 跟踪输出借用过来的。如您所见,默认为从最大视图状态到最小视图状态。要按 URL 或时间进行排序(两者都按升序排序),请单击列标题。
图 1 ViewStateStats.axd 输出的示例 (单击该图像获得较大视图)
在 ViewStateStats.axd 页面的上部可以控制视图状态数据收集。最值得关注的选项是第二个链接,收集控件视图状态大小。如果状态已禁用(默认选项),那么就只能收集页面的总大小。 单击链接将会切换收集状态以便为每个页面的最大总视图状态保持子控件的列表及其各自的视图状态。尽管这将会导致性能下降并占用更多内存,但是在很多情况下 都需要查明哪个控件正在占用视图状态。图 2 显示带有已展开控件的 ViewStateStats.axd 页面示例。
图 2 ViewStateStats.axd 控件树输出 (单击该图像获得较大视图)
正 如 ASP.NET 跟踪输出一样,控件树与控件的名称及其视图状态大小一起显示。如果您想知道呈现大小字节值和 ControlState 大小字节值所在的位置,我没有任何方法可获取该呈现数据,而且我感觉 ControlState 大小的计算会太慢。我将会在实现部分讨论原因。然而,由于视图状态是主要的性能杀手,所以真正重要的控件作用就在此。请注意,单个控件大小的总计不会是 URL 和时间旁边显示的总视图状态大小。因为视图状态是 Base64 编码的,所以编码会将额外的字节添加到整个视图状态中。
ViewStateStats.axd 标题下的第一个链接“清除所有视图状态统计”会刷新所有已保存的现有值并重新开始统计。最后一个链接“收集 HTTP GET 请求的视图状态大小”会打开针对 HTTP GET 请求的报告。由于您对视图状态的几乎所有担心都在于 HTTP POST,因此几乎不需要打开此选项。然而,如果需要同时查看 HTTP GET 和 HTTP POST 的视图状态大小,您就能够这么做。如果将此选项切换成开启,那么在 ViewStateStats.axd 页面中的每个 URL 下面都会出现一个 (GET) 或 (POST),具体视情况而定。当我讨论如何计算 HTTP GET 视图状态大小时,您将会看到记录此数据会对性能产生影响。

实现要点
当我第一次开始试着实现记录整个应用程序的视图状态大小这个想法时,我构建了一个派生 System.Web.UI.Page 类的原型。使用以下 C# 代码即可轻松地获取视图状态的字符串:
String viewState = Request.Params [ "__VIEWSTATE" ];
使 用派生 Page 的问题在于,对于许多现有的 ASP.NET 应用程序,需要更改代码才能查看整个应用程序的视图状态。由于 HttpModules 使您的代码能够参与正常的 ASP.NET 管道,所以这是一个完美的解决方案。因为我只想在页面准备好发送给用户之后检查该页面,所以我在 IHttpModule.Init 实现方法中设置了一个 EndRequest 事件处理程序。
在阅读有关 ASP.NET 视图状态的运行方式时,我有了一个有趣的发现,即以下 web.config 设置:
<pages maxPageStateFieldLength="XX"/>
在此,XX 是用于将 __VIEWSTATE 值分解为数据块的最大字节数。用 System.Web.Ui.Page.MaxPageStateFieldLength 属性也可以设置它。
ASP.NET 2.0 引入此设置的目的是帮助分解巨大的 __VIEWSTATE 值,因为有些防火墙和代理会被页面上的大型字符串阻塞。假如您希望避免大型字符串(也就是本专栏的中心思想),您可能会认为设置 maxPageStateFieldLength 可能会帮助减少一些大小。但实际上,它不但没有帮助,而且还会使传递给浏览器的页面变得臃肿。
如果在页面上具有如下的视图状态:
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwULLTE1ODEyMjM1MTgPZBYCAgMPZBYCAgcPEGQPFgNmAg­ECAhYDEAUBYQUBYWcQBQFiBQFiZxAFAW
MFAWNnZGRkQIU7yEIZAT5uuif4cZcFHBJgZ48=" />
</div>
将 maxPageStateFieldLength 设置为 50 会产生以下结果,在页面上的实际文本要多得多:
<div>
<input type="hidden" name="__VIEWSTATEFIELDCOUNT"
id="__VIEWSTATEFIELDCOUNT" value="3" />
<input type="hidden" name="__VIEWSTATE"
id="__VIEWSTATE" value="/wEPDwULLTE1ODEyMjM1MTgPZBYCAgMPZBYCAgcPEGQPFgNmAg" />
<input type="hidden" name="__VIEWSTATE1"
id="__VIEWSTATE1" value="ECAhYDEAUBYQUBYWcQBQFiBQFiZxAFAWMFAWNnZGRkQIU7yEIZ" />
<input type="hidden" name="__VIEWSTATE2"
id="__VIEWSTATE2" value="AT5uuif4cZcFHBJgZ48=" />
</div>
虽然可能有一些读者设置了 maxPageStateFieldLength,但是我怀疑是否有很多人在使用它。如果您将视图状态分解成数据块以绕过防火墙和代理问题,我强烈建议您考虑重新设计页面或将视图状态存储在服务器上,以避免通过线路发送大量的数据。
我 在 ViewStateStatisticsModule.cs 中实现的 HttpModule 可正确处理已设置了 maxPageStateFieldLength 的情况。因为必须要枚举每个 __VIEWSTATE* 参数,所以我使用的代码会比访问单一的 __VIEWSTATE 参数慢一些。
因 为数据收集和显示是逻辑耦合的,所以我决定将它们组合在 IViewStateProcessor 接口后面,然后从 HttpModule 和 HttpHandler 分离所有这些功能。IViewStateProcessor 接口提供处理 HTTP GET 和 HTTP POST 请求以及显示数据的方法。我的想法是能够在无需接触 Bugslayer.Web.dll 中大部分代码的情况下轻易地提供额外的数据收集和显示功能。
从 IViewStateProcessor 派生的类必须始终处于内存中,因此我将其存储在 ViewStateStatisticsModule 类内部的静态字段中。ViewStateStatisticsHandler 类只访问 ViewStateStatisticsModule 中的静态属性。目前,该代码只支持单个 IViewStateProcessor 派生类。一项不错的附加功能是增加对同时使用更多类的支持。
如 果选择指定您自定义的 IViewStateProcessor 派生类,那么将需要覆盖 HttpApplication.Init 方法。在那里,您将通过 HttpApplication.Modules["ViewStateStats"] 访问 ViewStateStatisticsModule。创建一个自定义 IViewStateProcessor 派生类的实例并将 ViewStateStatisticsModule.ViewStateProcessor 设置为您的实例。
收集和显示您在图 1 和图 2 中看到的数据的所有实际工作都是在默认的 IViewStateProcessor 派生类 BiggestPageProcessor 中进行的。收集数据存储在 Dictionary 类中,而 URL 本身则作为实现快速查找的关键字。收集代码中值得关注的部分是弄清楚如何获取单个控件的视图状态。
由 于每个人都习惯查看每个控件的视图状态大小,所以为了正确起见,我想要确保我的数字已经匹配。这意味着我需要查明操作在 ASP.NET 跟踪代码中发生的位置。Reflector 系列的第一站是 System.Web.Handlers.TraceHandler,它是为 trace.axd 注册的 HttpHandler。大约用了 15 分钟时间探究反编译代码和查询所引用的数据类型后,我终于面临 System.Web.Ui.Page.ProcessRequestMain,当 Page.Context.TracingEnabled 为 true 时,它执行实际的跟踪。在通过 Page.BuildPageProfileTree 之后,我最终到达 Control.BuildProfileTree,它是进行所有操作以使控件及其数据进入跟踪输出的位置。图 3 显示了反编译的代码。
protected void BuildProfileTree(string parentId, bool calcViewState)
{
int viewStateSize;
calcViewState = calcViewState && !this.flags[4];
if (calcViewState)
{
viewStateSize = this.EstimateStateSize(this.SaveViewState());
}
else
{
viewStateSize = 0;
}
int controlStateSize = 0;
if (((this.Page != null) &&
(this.Page._registeredControlsRequiringControlState != null)) &&
this.Page._registeredControlsRequiringControlState.Contains(
this))
{
controlStateSize =
this.EstimateStateSize(this.SaveControlStateInternal());
}
this.Page.Trace.AddNewControl(this.UniqueID, parentId,
base.GetType().FullName, viewStateSize, controlStateSize);
if ((this._occasionalFields != null) &&
(this._occasionalFields.Controls != null))
{
int count = this._occasionalFields.Controls.Count;
for (int i = 0; i < count; i++)
{
this._occasionalFields.Controls[i].BuildProfileTree(
this.UniqueID, calcViewState);
}
}
}

视 图状态代码中值得关注的部分是方法的前 10 行。真正使我困惑的第一部分代码是 !this.flags[4] 引用,因为 Reflector 将标志字段显示为私有。使用 Reflector 的应急 Analyzer,我跳转到标志字段并单击“Used By”树元素。扫描有关视图状态的任何方面都会显示 Control.EnableViewState 属性 getter 位于列表中。跳转到反编译过的 EnableViewState getter 方法会显示实际上该方法返回了 !this.flags[4],这样谜底就揭晓了。
EstimateStateSize 只是创建一个 ObjectStateFormatter、调用 Serialize 方法,然后返回序列化字节的大小,因此重复它很容易。当调用受保护的 this.SaveViewState 方法时,事情的确会更加有趣,而且我想要从 HttpModule 中调用它。正因为 .NET 反射是获取和调用任何对象的秘密武器,所以我略微使用,以确保得到的数字与 ASP.NET 跟踪系统相同。图 4 显示了我为计算单个控件视图状态大小编写的核心代码。
private PageViewState CreatePageViewState ( 
String url ,
DateTime timeStamp ,
Int32 viewStateLength ,
String httpRequestType )
{
PageViewState returnValue = new PageViewState (
url ,
timeStamp ,
viewStateLength ,
httpRequestType );
if ( true == collectControlData )
{
Page currentPage = HttpContext.Current.Handler as Page;
if ( null != currentPage )
{
CalculateControlSizes ( currentPage ,
0 ,
returnValue.ControlList );
}
}
return ( returnValue );
}

private static void CalculateControlSizes ( Control ctl ,
Int32 indentLevel ,
List<ControlViewState> ctlList )
{
Int32 viewStateSize = EstimateSize ( ctl );
ControlViewState state = new ControlViewState ( indentLevel ,
ctl.UniqueID ,
ctl.GetType ( ).FullName ,
viewStateSize );
// Add it to the list.
ctlList.Add ( state );
for ( int i = 0 ; i < ctl.Controls.Count ; i++ )
{
CalculateControlSizes ( ctl.Controls [ i ] , indentLevel + 1 ,
ctlList );
}
}

private static Int32 EstimateSize ( Control ctl )
{
// In Control.BuildProfileTree, the check is done against flag[4],
// which is the same as the implementation of EnableViewState.
if ( false == ctl.EnableViewState )
{
return ( 0 );
}
Object saved = CallSaveViewStateMethod ( ctl );
if ( null == saved )
{
return ( 0 );
}
ObjectStateFormatter osf = new ObjectStateFormatter ( );
Int32 ret = osf.Serialize ( saved ).Length;
return ( ret );
}

private static Object CallSaveViewStateMethod ( Control ctl )
{
// This is way ugly, but the SaveViewState method is protected on
// the Control class so in order to get the total size, I need to
// call it through Reflection.
Object ret = null;
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic;
Type type = ctl.GetType ( );
MethodInfo m = type.GetMethod ( "SaveViewState" , flags );
if ( null != m )
{
ret = m.Invoke ( ctl , null );
}
return ( ret );
}

private Int32 SumControlSizes ( Control ctl )
{
Int32 viewStateSize = EstimateSize ( ctl );
for ( int i = 0 ; i < ctl.Controls.Count ; i++ )
{
viewStateSize += SumControlSizes ( ctl.Controls [ i ] );
}
return ( viewStateSize );
}

在 弄清楚了如何计算视图状态(就象 ASP.NET 跟踪代码)的技巧后,便需要开始测试,以确定当我在 HttpModule.EndRequest 事件处理程序中要求得到视图状态值时,这些值是否相同。好消息是,在通过 Bugslayer.Web 运行所有种类的页面之后,我完全没有在跟踪报告和我计算所得的结果之间发现任何差异。
如果您好奇为什么我没有在显示中包含呈现大小字节数,那是因为我无法计算那些值。这种情况在页面呈现期间发生,而且我的 HttpModule 在管道中出现得太迟了。对于 ControlState 大小字节来说,出于性能原因,我选择不计算它们。图 3 显示了进行计算所需的工作。但是,我将不得不使用大量较慢的反射来进行这些调用,而收效却甚微。
目 光敏锐的读者可能已经注意到,我讨论了通过 Request.Params["__VIEWSTATE"] 获取视图状态是如何访问 HTTP GET 请求中并不存在的窗体变量的。但是,如果您在浏览器中查看一下页面的源代码,__VIEWSTATE 变量肯定会在那里,因此,如果用户直接通过浏览器的地址栏转到某个页面,我想要在这种情况下找到一种获取数据的方法。
经 过充分的斟酌之后,我想到的最佳解决方案就是亲自进行计算,即循环访问控件并获取每个视图状态大小。尽管并不完美,但是它和实际视图状态大小很接近,而且 无需进行完整的 BASE64 编码,且不会引起额外的开销。由于 HTTP GET 处理的这种限制,我将监控它的选项保留为在默认情况下处于关闭状态。

深入设想
整 体而言,我对视图状态监控工具的效果十分满意。通过简化与现有生产应用程序的集成,我要查询这些臃肿视图状态的目标应该更容易实现。当视图状态中存在一个 无声的性能杀手时,能够轻松查看页面视图状态大小意味着调试会更快,这样您便可以致力于研究新功能和一些有趣的方面,也正是我们从事这项工作的原因。
和所有的 Bugslayer 专栏一样,您总是可以给 Bugslayer.Web 增加一些功能,以便跟踪更多有关视图状态状况的信息。
本 专栏提供的工具版本仅跟踪页面上以 __VIEWSTATE 隐藏字段形式存在的视图状态,因为这是所有性能杀手中最糟糕的。您可以考虑扩展 Bugslayer.Web 以跟踪存储在服务器上的视图状态信息。这将会需要更多的工作,因为需要提供自定义的 Page 派生类来覆盖 Page.SaveViewState 方法。
在该示例中,视图状态存储仅以每个 HttpApplication 为基础。一项不错的附加功能是跟踪 Web 场中最大的视图状态。
由于我想要让控件树类似于 ASP.NET 跟踪所显示的内容,因此我会包含无任何视图状态数据的控件。一个快速优化方案是只收集那些对视图状态产生影响的控件。但更好的做法是,在 ViewStateStats.axd 处理程序中使它成为可切换的选项。
如果您是一位真正的 HTML 和 JavaScript 天才,您当然可以使 ViewStateStats.axd 页面显示得更好。尽管我选择最简单的显示形式,但是拥有可扩展和可折叠的控件显示将会使查看非常大的网站数据更加容易。
最后,作为个人照会,我要衷心地感谢 10 年以来阅读 Bugslayer 专栏的所有读者。这些年你们提出的所有问题和意见已使得撰写这些专栏比你们想象的更充满乐趣!
提示 80 最 近我正在解决一个问题,即 IIS 中的某个应用程序池被意外回收。在拼命搜索 Internet 的过程中,我发现了一个非常棒的博客记录“Common reasons why your application pool may unexpectedly recycle”(应用程序池可能会意外回收的常见原因),作者是 Johan Straarup,网址为 blogs.msdn.com/johan/archive/2007/05/16/common-reasons-why-your-application-pool-may-unexpectedly-recycle.aspx。它出色地概括了当 W3WP.EXE 出差错时您想要知道的所有原因。Johan 的博客是值得订阅的。
提示 81 想要成为更优秀的调试人员?Roberto Farah 忘记的有关 WinDBG 的知识远比我们任何人希望了解的要多得多,他整理了我迄今为止见过的最好的调试书籍编目之一,网址为 blogs.msdn.com/debuggingtoolbox/archive/2007/06/08/recommended-books-how-to-acquire-or-improve-debugging-skills.aspx。无论您在 Windows 上从事哪种类型的开发,Roberto 均有涉及。在 Roberto 的博客中,他正在处理将完全超出您想象的 WinDBG 脚本!我个人最喜爱的是他挫败扫雷游戏的 WinDBG 脚本 (blogs.msdn.com/debuggingtoolbox/archive/2007/03/28/windbg-script-playing-with-minesweeper.aspx)。现在我是常胜将军了!
提示 82 如果您曾阅读过以前的 Bugslayer 专栏,您便会知道我非常痴迷于 MSBuild 任务 (msdn.microsoft.com/msdnmag/issues/06/03/Bugslayer)。 您应该通过 MSBuild 自动化整个世界。SDC Tasks 已得到更新,它包含了 300 多个任务,其中包括创建网站、应用程序池和 Active Directory 用户、运行 FxCop、配置虚拟服务器、创建 zip 文件、配置 COM+、创建文件夹共享、安装到 GAC、配置 SQL Server 以及配置 BizTalk® Server 2004 与 BizTalk Server 2006。要按照您从自动生成所了解的内容对生活进行控制,请从 codeplex.com/sdctasks 下载新任务。

请将您想向 John 询问的问题和提出的意见发送至 slayer@microsoft.com.


John Robbins是 Wintellect 的共同创始人之一,这是一家致力于 .NET 和 Windows 平台的软件咨询、培训和开发公司。他的最新著作是“Debugging Microsoft .NET 2.0 Applications”(调试 Microsoft .NET 2.0 应用程序)(Microsoft Press, 2006)。您可以通过访问 wintellect.com 与 John 取得联系。
posted on 2009-09-25 17:48  Robin99  阅读(706)  评论(0编辑  收藏  举报