元のアドレス: http://blogs.msdn.com/tess/archive/2006/10/10/ASP.NET-Crash-_2D00_-Crazy-looping-in-a-SiteMap.aspx
公開日: 2006 年 10 月 10 日火曜日、午後 4 時 10 分
著者: Tess
ある日、私のブログについて次のような質問をするメールを受け取りました。簡単に説明すると、次のとおりです。
サイトマップをすばやく作成したかったので、BuildSiteMap() メソッドをオーバーライドし、その中に偽のサイトマップ ノードを追加するループを作成しました。
パブリック オーバーライド SiteMapNode BuildSiteMap(){
for (int i = 0; i < 5; i++)
myRoot.ChildNodes.Add(new SiteMapNode(this, i.ToString(), i.ToString(), i.ToString()));
myRoot を返します。
、
スタック オーバーフローが発生し、サーバーがクラッシュします。デバッガーを使用してステップ実行したところ、非常に奇妙なものを発見しました:
1) int i = 0
2) i < 5
3) myRoot...
4) int i = 0
5) i < 5
SiteMapNode を呼び出す (プロパティにアクセスし、メソッドを呼び出す) まで i の値は決して増加しないように見えます。これはループが正しいように見えます
。
このループが不定になるのはなぜですか?一見すると、コンパイラまたは CLR のバグかもしれません
(この問題が発生したとき、私は ASP.NET 2.0 のサイト ナビゲーションについてまったく知りませんでしたが、次の記事を見つけました。http ://weblogs.asp. net/scottgu/archive/2005/11/20/431019.aspxおよびhttp://aspnet.4guysfromrolla.com/articles/111605-1.aspx の説明は非常に優れています)。
最初の考え
この問題で最も重要なことは、常に再起動されるということです。つまり、その場でデバッグできるということです。しかし、まだそこまでは行かずに、振り返って今の状況を見てみましょう...
1. スタックオーバーフロー
2. 何度も始まるサイクル
スタック オーバーフローについては、以前のブログ投稿で説明しましたので、もう一度繰り返します。スタック オーバーフローは、関数ポインタ、変数ポインタ、およびパラメータが多すぎて割り当てられ、スタックに割り当てられたメモリの量が不足した場合に発生します。十分。スタック オーバーフローの最も一般的な原因は、終わらない再帰です。つまり、関数 A は関数 B を呼び出し、関数 B は関数 A を呼び出します...
コールスタックは次のようになります...
...
関数B()
関数A()
関数B()
関数A()
まあ、それはそれでいいのですが、これはスタック オーバーフローの説明にすぎません。では、この狂ったサイクルで何が起こっているのでしょうか?
OK...そのような関数があると想像してください (--> にブレークポイントがあります)
void MyRecursiveFunction(){
for(int i=0; i<5; i++){
--> MyRecursiveFunction();
}
}
最初にブレークポイントで停止したとき、i の値は 0 であるはずで、コールスタックは次のようになります...
MyRecursiveFunction()
...
ここで、MyRecrusive 関数を呼び出します。関数自体が呼び出されるたびに、i=0 が再び表示されます (ただし、実際には同じループ内ではありません)。 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 の値は変更されていないように見えます。現時点では、スタック呼び出しを実際に見るまでは、これについては深く理解できません。
コールスタックを見ると、コールスタックは次のようになります...
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
...
最初の考えの結論は、間違いなく再帰を調べたいということです...しかしどこでしょうか?例のコード
myRoot.ChildNodes.Add(new SiteMapNode(this, i.ToString(), i.ToString(), i.ToString()));
ここで最も疑わしいのは次の点です
。
new SiteMapNode() と myRoot.ChildNodes.Add() ですが、これをリフレクターで見ると、それほど不思議ではなくなります。
デバッグの問題
最後に:) 言葉を減らして、Windbg アクションを増やします...
再レンダリングは簡単にできるので、w3wp.exe に Windbg (ファイル / プロセスにアタッチ) をアタッチし、g を押して開始するだけで、自分のマシン上で再レンダリングします。その後、問題が再発し、プログラムが異常終了し、スタック オーバーフローであると通知されます (これはすでにわかっています)。
(7e4.ddc): スタック オーバーフロー - コード c00000fd (最初のチャンス)
初回例外は、例外処理の前に報告されます。
この例外は予期され、処理される可能性があります。
eax=0fa4235c ebx=02beca74 ecx=02beca74 edx=02becb54 esi=02becb54 edi=02beca74
eip=686b5cb4 esp=02163000 ebp=02163004 iopl=0 nv アップ 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> !clrスタック
OS スレッド ID: 0xddc (16)
ESP EIP 02163000 686b5cb4 System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode)
... これはあまり役に立ちません。スタック オーバーフローが発生すると、!clrstack コマンドを使用すると、そのような問題が発生することがあります。したがって、生のスタックを表示するには、!dumpstack コマンドも使用する必要があります。
0:016> !ゴミ箱
OS スレッド ID: 0xddc (16)
現在のフレーム: (MethodDesc 0x68b03720 +0x4 System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode))
ChildEBP RetAddr 呼び出し元、呼び出し先
02163004 686b1fc4 (メソッド記述 0x68aeff30 +0x18 System.Web.SiteMapNode.get_ChildNodes())
0216300c 0f765641 (メソッド記述 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 (メソッド記述 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 (メソッド記述 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 関数が再度呼び出され、さらに BuildSiteMap 関数が ChildNodes プロパティを呼び出すなど、スタック オーバーフローが発生します。
結論は
BuildSitemap に関するドキュメントには、次の段落があります。
BuildSiteMap メソッドは、FindSiteMapNode、GetChildNodes、および GetParentNode メソッドのデフォルト実装によって呼び出されます。派生クラスで BuildSiteMap メソッドをオーバーライドする場合は、サイト マップ データを 1 回だけ読み込み、それ以降の呼び出しでそれを返すようにしてください。
再帰とスタック オーバーフローを回避するには、このメソッドの呼び出しを避けることが最善です。BuildSiteMap の例と同様に、AddNode メソッドを使用して子ノードを追加できます。
これは、「サイト マップ プロバイダー」の記事にアーカイブされており、読む価値があります。
多くのメソッドとプロパティはデフォルトで BuildSiteMap 呼び出しを実装するため、BuildSiteMap は通常、他のサイト マップによって提供されるメソッドやプロパティを呼び出すべきではありません。 たとえば、BuildSiteMap の RootNode は再帰を引き起こし、スタック オーバーフローで終了します。
http://www.cnblogs.com/Ring1981/archive/2006/10/19/443280.html