ASP.NET 崩潰-SiteMap中瘋狂的迴圈

來源:互聯網
上載者:User
原文地址:ASP.NET Crash - Crazy looping in a SiteMap
發布時間: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 方法由 FindSiteMapNodeGetChildNodesGetParentNode方法的預設實現調用。如果在衍生類別中重寫 BuildSiteMap 方法,請確保它僅載入一次網站地圖資料,並在後續調用中返回。 為了避免出現遞迴和堆疊溢位,最好避免調用該方法,像在BuildSiteMap例子裡,我們可以用AddNode方法來添加子節點。 這在歸檔在Site Map Providers這篇文章中,該文同樣值得一讀。 BuildSiteMap 一般不應當調用其他的site map提供的方法或屬性,因為許多方法和屬性預設會實現BuildSiteMap調用。 例如,BuildSiteMap中的RootNode會引起遞迴,從而使之以堆疊溢位而終止。   

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.