Ursprüngliche Adresse: http://blogs.msdn.com/tess/archive/2006/10/10/ASP.NET-Crash-_2D00_-Crazy-looping-in-a-SiteMap.aspx
Veröffentlicht: Dienstag, 10. Oktober 2006, 16:10 Uhr
Autor: Tess
Eines Tages erhielt ich eine E-Mail zu meinem Blog mit den folgenden Fragen, die kurz wie folgt beschrieben wurden:
Ich wollte schnell eine Sitemap erstellen, also habe ich die BuildSiteMap()-Methode überschrieben und darin eine Schleife geschrieben, die einige gefälschte Sitemap-Knoten hinzufügte.
öffentliche Überschreibung von SiteMapNode BuildSiteMap(){
for (int i = 0; i < 5; i++)
myRoot.ChildNodes.Add(new SiteMapNode(this, i.ToString(), i.ToString(), i.ToString()));
return myRoot;
}
Beim Ausführen des Programms kommt es zu einem Stapelüberlauf und der Server stürzt ab. Ich habe den Debugger verwendet, um durchzugehen, und habe etwas wirklich Seltsames gefunden:
1) int i = 0
2) i < 5
3) myRoot...
4) int i = 0
5) ich < 5
usw.
Der Wert von i scheint sich nie zu erhöhen, es sei denn, ich rufe den SiteMapNode auf (auf eine Eigenschaft zugreifen, eine Methode aufrufen), was so aussieht, als wäre die Schleife korrekt.
Was macht diese Schleife unbestimmt? Auf den ersten Blick könnte es sich um einen Compiler- oder CLR-Fehler handeln.
(Als ich dieses Problem hatte, wusste ich wirklich nichts über die Site-Navigation in ASP.NET 2.0, aber ich habe diese Artikel gefunden ... http://weblogs. asp. (net/scottgu/archive/2005/11/20/431019.aspx und http://aspnet.4guysfromrolla.com/articles/111605-1.aspx , die Beschreibungen sind wirklich gut.)
erste Gedanken
Das Wichtigste an diesem Problem ist, dass es immer neu startet, was bedeutet, dass es sofort behoben werden kann. Aber gehen wir noch nicht so weit, schauen wir zurück und sehen, was wir jetzt haben ...
1. Stapelüberlauf
2. Ein Kreislauf, der immer wieder beginnt
Ich habe den Stapelüberlauf in einem früheren Blog-Beitrag besprochen und werde ihn wiederholen ... Ein Stapelüberlauf tritt auf, wenn zu viele Funktionszeiger, Variablenzeiger und Parameter zugewiesen werden, sodass die auf dem Stapel zugewiesene Speichermenge nicht zunimmt genug. Die mit Abstand häufigste Ursache für Stapelüberläufe ist das Unterbrechen der Rekursion. Mit anderen Worten: Funktion A ruft Funktion B auf, Funktion B ruft Funktion A auf ...
Der Callstack sieht also ungefähr so aus ...
...
FunktionB()
FunktionA()
FunktionB()
FunktionA()
Okay, das ist alles schön und gut, aber das erklärt nur den Stapelüberlauf. Was ist also mit diesem verrückten Zyklus los?
OK ... stellen Sie sich vor, es gibt eine solche Funktion (es gibt einen Haltepunkt bei -->)
void MyRecursiveFunction(){
for(int i=0; i<5; i++){
-> MyRecursiveFunction();
}
}
Wenn Sie zum ersten Mal am Haltepunkt anhalten, sollte der Wert von i 0 sein und der Aufrufstapel sieht so aus ...
MyRecursiveFunction()
...
Rufen Sie nun die Funktion MyRecrusive auf. Jedes Mal, wenn die Funktion selbst aufgerufen wird, wird i=0 erneut angezeigt (obwohl wir uns nicht wirklich in derselben Schleife befinden). Wenn Sie die MyRecrusive-Funktion mehrmals aufrufen und durch den tatsächlich ausgeführten Code ersetzen, wird ein Code ähnlich dem folgenden ausgeführt:
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++){
...
}
}
}
}
}
}
}
... Wenn man es in Visual Studio betrachtet, sieht es so aus, als ob immer dieselbe Schleife ausgeführt wird und der Wert der Variablen i nicht geändert wird. Im Moment werden Sie dies erst verstehen, wenn Sie den Stack-Aufruf tatsächlich sehen.
Wenn wir uns den Callstack ansehen, sieht der Callstack jetzt so aus ...
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
MyRecursiveFunction()
...
die Schlussfolgerung des ersten Gedankens ist also, dass wir uns zweifellos eine Rekursion ansehen wollen ... aber wo? Der Code im Beispiel
myRoot.ChildNodes.Add(new SiteMapNode(this, i.ToString(), i.ToString(), i.ToString()));
scheint nicht so kompliziert zu sein...
Das Verdächtigste hier ist neu SiteMapNode() und myRoot.ChildNodes.Add(), das ist nicht mehr so mysteriös, wenn wir es mit Reflektor betrachten.
Debugging-Probleme
Endlich:) Weniger Worte, mehr Windbg-Action ...
Da es problemlos neu gerendert werden kann, rendere ich es auf meinem Computer erneut, indem ich einfach windbg (Datei/An Prozess anhängen) an w3wp.exe anhänge und zum Starten auf g drücke. Das Problem tritt dann erneut auf und das Programm bricht ab und teilt mir mit, dass es sich um einen Stapelüberlauf handelt (was wir bereits wissen).
(7e4.ddc): Stapelüberlauf – Code c00000fd (erste Chance)
Ausnahmen der ersten Chance werden vor der Ausnahmebehandlung gemeldet.
Diese Ausnahme kann erwartet und behandelt werden.
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
Lassen Sie uns den Stapel überprüfen und mit dem Befehl !clrstack sehen, wie er abgebrochen wurde, aber wir können nur sehen ....
0:016> !clrstack
Betriebssystem-Thread-ID: 0xddc (16)
ESP EIP 02163000 686b5cb4 System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode)
... Das hilft uns nicht wirklich viel. Manchmal, wenn wir auf einen Stapelüberlauf stoßen, treten solche Probleme bei der Verwendung des Befehls !clrstack auf. Daher müssen wir auch den Befehl !dumpstack verwenden, um den Rohstapel anzuzeigen.
0:016> !dumpstack
Betriebssystem-Thread-ID: 0xddc (16)
Aktueller Frame: (MethodDesc 0x68b03720 +0x4 System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode))
ChildEBP RetAddr Anrufer, Angerufener
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())
...
Okay, es sieht so aus, als ob das Problem bei der ChildNodes-Eigenschaft liegt. Wenn diese Eigenschaft verwendet wird, wird die Funktion „GetChildNodes“ aufgerufen, die erneut die Funktion „BuildSiteMap“ aufruft, die wiederum die Eigenschaft „ChildNodes“ aufruft usw., was zu einem Stapelüberlauf führt.
abschließend
In der Dokumentation zu BuildSitemap finden Sie folgenden Absatz:
Die BuildSiteMap-Methode wird von der Standardimplementierung der Methoden FindSiteMapNode, GetChildNodes und GetParentNode aufgerufen. Wenn Sie die BuildSiteMap-Methode in einer abgeleiteten Klasse überschreiben, stellen Sie sicher, dass sie die Sitemap-Daten nur einmal lädt und sie bei nachfolgenden Aufrufen zurückgibt.
Um Rekursion und Stapelüberlauf zu vermeiden, vermeiden Sie am besten den Aufruf dieser Methode. Wie im BuildSiteMap-Beispiel können wir die AddNode-Methode verwenden, um untergeordnete Knoten hinzuzufügen.
Dies ist im Artikel Site Map Providers archiviert, der ebenfalls lesenswert ist.
BuildSiteMap sollte im Allgemeinen keine Methoden oder Eigenschaften aufrufen, die von anderen Sitemaps bereitgestellt werden, da viele Methoden und Eigenschaften BuildSiteMap-Aufrufe standardmäßig implementieren. Beispielsweise verursacht der RootNode in BuildSiteMap eine Rekursion, die zu einem Stapelüberlauf führt.
http://www.cnblogs.com/Ring1981/archive/2006/10/19/443280.html