打印

[asp.net教程] asp.net 崩溃-sitemap中疯狂的循环

asp.net 崩溃-sitemap中疯狂的循环

原文地址:http://blogs.msdn.com/tess/archive/2006/10/10/asp.net-crash-_2d00_-crazy-looping-in-a-sitemap.aspx
  发布时间:tuesday, october 10, 2006 4:10 pm
  作  者:tess
   
  一天,我收到了一封有关我的博客的邮件,提出如下问题,简述如下:
   
  我想快速地创建一个站点地图,因此我重写了buildsitemap()方法,在里面我写了一个循环,用以添加一些仿造的sitemap节点。
   
   
  public override sitemapnode buildsitemap(){
   for (int i = 0; i < 5; i++)
   myroot.childnodes.add(new sitemapnode(this, i.tostring(), i.tostring(), i.tostring()));
   return myroot;
  }
   
  运行程序,就发生堆栈溢出,服务器也崩溃了。我用调试器单步调试,发现真的很奇怪:
   
  1) int i = 0
  2) i < 5
  3) myroot...
  4) int i = 0
  5) i < 5
  etc.
   
  i的值看起来从来没有增加,除非我调用到sitemapnode(access a property, call a method),看起来这个循环是正确的。
  是什么使得这个循环不确定呢?咋看可能是编译器或者是clr的一个bug.
   
  (当我获此问题时,我真不知道asp.net2.0中的站点导航,但我找到了这些文章... http://weblogs.asp.net/scottgu/archive/2005/11/20/431019.aspx 和http://aspnet.4guysfromrolla.com/articles/111605-1.aspx ,叙述得真是很不错.)
   
   
   
  最初的想法
   
   
  这个问题最重要的就是它始终重新开始, 这就意味着可以对此做现场调试。但我们暂不走那么远,先回头看看现在有什么...
   
   
  1. 堆栈溢出
   
   
  2. 一次又一次重新开始的循环
   
   
   我已经在先前的博客帖子里讨论过堆栈溢出,现在重复一下... 引起堆栈溢出的原因是, 分配了太多的函数指针,变量指针和参数,以致在堆栈里申请的内存数量不够用。到目前为止,堆栈溢出最平常的原因是无终止的递归。换句话说,function a调用了function b, function b又调用了function a...
   
   
  因此,callstack看上去有点像这样....
   
   
  ...
  functionb()
  functiona()
  functionb()
  functiona()
   
   
  好了,一切都好极了,但那仅仅解释了堆栈溢出。那么疯狂的循环是怎么回事呢?
   
  好...想象一下有这样一个函数(在-->处有有一个断点)
   
   
  void myrecursivefunction(){
   for(int i=0; i<5; i++){
  --> myrecursivefunction();
   }
  }
   
   
  当你第一次停在断点处,i的值应该是0,callstack看起来是这样的...
   
   
  myrecursivefunction()
  ...
   
   
   
  现在调用myrecrusive函数,每一次调用这个函数自己,会再一次出现 i=0(虽然我们并不真的在同一个loop里)。若调用myrecrusive这个函数几个来回,并用实际执行的代码代替之,它将执行类似如下的代码:
   
   
  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++){
   ...
   }
   }
   }
   }
   }
   }
  }
   
   
  ... 在visual studio中查看它,看起来总是运行同样的循环,且并不改变变量i的值。暂时,你对此不会有深层次的理解,直到你真正看到堆栈调用。
  假如我们看一下callstack, callstack现在看起来是这样的...
   
  myrecursivefunction()
  myrecursivefunction()
  myrecursivefunction()
  myrecursivefunction()
  myrecursivefunction()
  myrecursivefunction()
  myrecursivefunction()
  ...
   
  因此最初想法的结论是,我们无疑地要看看某些递归...但在哪呢?例子中的代码
   
   myroot.childnodes.add(new sitemapnode(this, i.tostring(), i.tostring(), i.tostring()));
   
  看起来并不是那么复杂...
   
  在这儿最可疑的是new sitemapnode() 和myroot.childnodes.add() ,假如我们用reflector查看一下,那么这将不再那么神秘。
   
   
  调试问题
   
   
  最后 少一点口舌,多来一点windbg行动...
   
   
  因它易重新呈现,所以我会在我的机器上重新呈现它,我只要将windbg (file / attach to process)附到w3wp.exe上,点击g开始即可。然后会重新产生了这个问题,程序中止时提示我这是一个堆栈溢出(我们已经知道了)。
   
  (7e4.ddc): stack overflow - code c00000fd (first chance)
  first chance exceptions are reported before any exception handling.
  this exception may be expected and handled.
  eax=0fa4235c ebx=02beca74 ecx=02beca74 edx=02becb54 esi=02becb54 edi=02beca74
  eip=686b5cb4 esp=02163000 ebp=02163004 iopl=0 nv up ei pl zr na pe nc
  cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210246
  system_web_ni+0xf5cb4:686b5cb4 56 push esi
   
  我们查看一下堆栈,使用 !clrstack命令看看是怎么中止的,但我们只能看到....
   
   
  0:016> !clrstack
  os thread id: 0xddc (16)
  esp eip 02163000 686b5cb4 system.web.staticsitemapprovider.getchildnodes(system.web.sitemapnode)
   
   ... 这对我们并没有太大的帮助。有时当我们遇到堆栈溢出时,使用!clrstack 命令就会出现一些这样的问题。因此我们还需要使用!dumpstack命令查看一下raw stack。
   
   
  0:016> !dumpstack
  os thread id: 0xddc (16)
  current frame: (methoddesc 0x68b03720 +0x4 system.web.staticsitemapprovider.getchildnodes(system.web.sitemapnode))
  childebp retaddr caller,callee
  02163004 686b1fc4 (methoddesc 0x68aeff30 +0x18 system.web.sitemapnode.get_childnodes())
  0216300c 0f765641 (methoddesc 0xfa42328 +0x59 viewsitemapprovider.buildsitemap())
  0216303c 686b5cdf (methoddesc 0x68b03720 +0x2f system.web.staticsitemapprovider.getchildnodes(system.web.sitemapnode))
  02163074 686b1fc4 (methoddesc 0x68aeff30 +0x18 system.web.sitemapnode.get_childnodes())
  0216307c 0f765641 (methoddesc 0xfa42328 +0x59 viewsitemapprovider.buildsitemap())
  021630ac 686b5cdf (methoddesc 0x68b03720 +0x2f system.web.staticsitemapprovider.getchildnodes(system.web.sitemapnode))
  021630e4 686b1fc4 (methoddesc 0x68aeff30 +0x18 system.web.sitemapnode.get_childnodes())
  021630ec 0f765641 (methoddesc 0xfa42328 +0x59 viewsitemapprovider.buildsitemap())
  0216311c 686b5cdf (methoddesc 0x68b03720 +0x2f system.web.staticsitemapprovider.getchildnodes(system.web.sitemapnode))

  02163154 686b1fc4 (methoddesc 0x68aeff30 +0x18 system.web.sitemapnode.get_childnodes())
  0216315c 0f765641 (methoddesc 0xfa42328 +0x59 viewsitemapprovider.buildsitemap())
  ...
   
  好了,这看起来问题出自childnodes属性。使用该属性时,会调用getchildnodes 函数,这个函数会再次调用buildsitemap 函数,从而它又调用了childnodes 属性,如此一直下去,导致了堆栈溢出。
   
   
  结论
   
   
   在关于buildsitemap的文档中,你能找到如下段落:
   
  buildsitemap 方法由 findsitemapnode、getchildnodes和getparentnode方法的默认实现调用。如果在派生类中重写 buildsitemap 方法,请确保它仅加载一次站点地图数据,并在后续调用中返回。
  为了避免出现递归和堆栈溢出,最好避免调用该方法,像在buildsitemap例子里,我们可以用addnode方法来添加子节点。
  这在归档在site map providers这篇文章中,该文同样值得一读。
  buildsitemap 一般不应当调用其他的site map提供的方法或属性,因为许多方法和属性默认会实现buildsitemap调用。 例如,buildsitemap中的rootnode会引起递归,从而使之以堆栈溢出而终止。
  http://www.cnblogs.com/ring1981/archive/2006/10/19/443280.html

TOP

返回顶部
AYBlue

Processed in 0.067625 second(s), 7 queries.

当前时区 GMT+8, 现在时间是 2009-1-10 11:45 京ICP备06054220号

清除 Cookies - 联系我们 - 163K.com - Archiver - WAP