ASP.NET崩溃 - SiteMap里的疯狂循环(概译)

本文通过一个站点地图创建过程中的无限循环导致堆栈溢出的问题,深入探讨了.NET框架中站点地图节点递归调用机制及其引发的问题,并提供了解决方案。

作者:tess
原文地址:ASP.NET Crash - Crazy looping in a SiteMap

本文只是一个快速的概括性翻译, 如果敢兴趣还请您看原版.

我想快速创建一个并不美观的站点地图(site map), 所以我只简单的在我override的BuildSiteMap()函数里使用一个小循环添加了几个地图节点(sitemap node)

ExpandedBlockStart.gif ContractedBlock.gif public   override  SiteMapNode BuildSiteMap() dot.gif {
InBlock.gif   
for (int i = 0; i < 5; i++)
InBlock.gif      myRoot.ChildNodes.Add(
new SiteMapNode(this, i.ToString(), i.ToString(), i.ToString()));
InBlock.gif   
return myRoot;
ExpandedBlockEnd.gif}

当我运行这个web应用程序的时候我得到的却是"堆栈溢出(Stack overflow)",然后服务器就崩溃了. 然后我借助调试器调试这段代码,我所看到的东西非常奇怪:

None.gif 1 int  i  =   0
None.gif
2 ) i  <   5
None.gif
3 ) myRootdot.gif
None.gif
4 int  i  =   0
None.gif
5 ) i  <   5
None.gifetc. 
None.gif

看起来i的值并没有增加,是编译器的bug还是CLR的?(暂时撇开sitemap的内部机制, 因为sitemap从设计上不允许我们这么使用,但是这个条语句你却可以随便写的)

在debug之前我们向后退几步重审一下:

  1. 堆栈溢出
  2. 一个看起来像是无尽的循环

造成堆栈溢出是因为我们已经占用了大量的内存,而它们是当初为了在栈上分配太多的指向指向局部变量或参数的指针而服务的.(we have exceeded the amount of memory reserved for the stack by allocating too many function pointers, pointers to local vars and parameters on the stack.)但是往往我们都是因为死循环(never-ending recursion)而造成的, 换言之, funcitonA()调用了functionB(),而functionB()里又调用了functionA().

ExpandedBlockStart.gif ContractedBlock.gif void  MyRecursiveFunction() dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif     
for(int i=0; i<5; i++)dot.gif{
InBlock.gif
-->      MyRecursiveFunction();
ExpandedSubBlockEnd.gif     }

ExpandedBlockEnd.gif}
 

所以此时我们的调用栈(callstack)看起来应该是这样的:

None.gif functionB()
None.giffunctionA()
None.giffunctionB()
None.giffunctionA()

好,那我们现在想象一下如果你现在有这样的函数:

当你第一次在断点位置上停止的时候i的值应该是0,调用栈看起来应该是

MyRecursiveFunction()
...

现在我们用另一种方式执行进入函数内部(其实还是它本身)

for ( int  i = 0 ; i < 5 ; i ++ ){
   
for ( int  i2 = 0 ; i2 < 5 ; i2 ++ ){
      
for ( int  i3 = 0 ; i3 < 5 ; i3 ++ ){
         
for ( int  i4 = 0 ; i4 < 5 ; i4 ++ ){
            
for ( int  i5 = 0 ; i5 < 5 ; i5 ++ ){
               
for ( int  i6 = 0 ; i6 < 5 ; i6 ++ ){
                  
for ( int  i7 = 0 ; i7 < 5 ; i7 ++ ){
                     dot.gif
                  }
               }
            }
         }
      }
   }
}

所以调用栈应该是这样的:

MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
dot.gif 

调试:

将windbg附属到w3wp.exe的进程上(文件/附属到进程),按下g运行.程序一会就终止了显示出了下列信息,表明是堆栈溢出(就像我们已经知道的一样)

 
 

如果我们用!clrstack看一下堆栈来确定我们是怎么结束的,我们只能看到这个:

 
 

可惜的这些不能告诉我们更多的东西, 当我们陷入堆栈溢出错误的时候!clrstack有时会在列举堆栈信息上出现一些问题,所以我们必须使用!dumpstack来看一下.
(注意:!dumpstack不展示真实的堆栈,有一些函数可能是错误的,但是它能很好的让我们知道究竟怎么了)

 
 

Ok,看起来问题出在ChildNodes属性上了, 它调用GetChildNodes()函数,而GetChildNodes()又再次调用我们的BuildSiteMap函数, 该函数调用了ChildNodes属性, 所以这样造成一个死循环.

结论:

创建站点地图一文中你可以找到答案和解决文章开始时问题的正确处理方法.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值