一天,我收到了一封有關我的博客的郵件,提出如下問題,簡述如下:
我想快速地創建一個站點地圖,因此我重寫了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()
...