Dirección original: http://blogs.msdn.com/tess/archive/2006/10/10/ASP.NET-Crash-_2D00_-Crazy-looping-in-a-SiteMap.aspx
Publicado: martes, 10 de octubre de 2006 16:10
Autor: Tess
Un día, recibí un correo electrónico sobre mi blog en el que se hacían las siguientes preguntas, que se describen brevemente de la siguiente manera:
Quería crear un mapa del sitio rápidamente, así que anulé el método BuildSiteMap() y dentro de él escribí un bucle que agregaba algunos nodos de mapa del sitio falsos.
anulación pública SiteMapNode BuildSiteMap(){
para (int i = 0; i < 5; i++)
myRoot.ChildNodes.Add(new SiteMapNode(this, i.ToString(), i.ToString(), i.ToString()));
devolver miRoot;
}
Al ejecutar el programa, se produce un desbordamiento de la pila y el servidor falla. Utilicé el depurador para revisar y encontré algo realmente extraño:
1) int i = 0
2) yo < 5
3) mi raíz...
4) int yo = 0
5) yo < 5
etc.
El valor de i nunca parece aumentar a menos que llame al SiteMapNode (acceda a una propiedad, llame a un método), lo que parece que el bucle es correcto.
¿Qué hace que este bucle sea indeterminado? A primera vista, podría ser un error del compilador o de CLR
(cuando tuve este problema, realmente no sabía acerca de la navegación del sitio en ASP.NET 2.0, pero encontré estos artículos... http://weblogs.asp. net/scottgu/archive/2005/11/20/431019.aspx y http://aspnet.4guysfromrolla.com/articles/111605-1.aspx , las descripciones son realmente buenas).
pensamientos iniciales
Lo más importante de este problema es que siempre se reinicia, lo que significa que se puede depurar en el acto. Pero no vayamos tan lejos todavía, miremos hacia atrás y veamos lo que tenemos ahora...
1. Desbordamiento de pila
2. Un ciclo que comienza una y otra vez
Hablé sobre el desbordamiento de pila en una publicación de blog anterior y lo repetiré... El desbordamiento de pila ocurre cuando se asignan demasiados punteros de función, punteros de variable y parámetros, de modo que la cantidad de memoria asignada en la pila no es suficiente. Con diferencia, la causa más común de desbordamientos de pila es la recursividad ininterrumpida. En otras palabras, la función A llama a la función B, la función B llama a la función A...
Entonces la pila de llamadas se parece un poco a esto...
...
funciónB()
funciónA()
funciónB()
funciónA()
Bien, eso está muy bien, pero eso solo explica Stack Overflow. Entonces, ¿qué está pasando con este ciclo loco?
OK...imagina que existe tal función (hay un punto de interrupción en -->)
anular MiFunciónRecursiva(){
para(int i=0; i<5; i++){
--> MiFunciónRecursiva();
}
}
Cuando se detiene por primera vez en el punto de interrupción, el valor de i debe ser 0 y la pila de llamadas se verá así...
MiFunciónRecursiva()
...
Ahora llame a la función MyRecrusive. Cada vez que se llame a la función, i=0 aparecerá nuevamente (aunque en realidad no estamos en el mismo ciclo). Si llama a la función MyRecrusive varias veces y la reemplaza con el código real que se ejecuta, ejecutará un código similar al siguiente:
para(int i=0; i<5; i++){
para(int i2=0; i2<5; i2++){
para(int i3=0; i3<5; i3++){
para(int i4=0; i4<5; i4++){
para(int i5=0; i5<5; i5++){
para(int i6=0; i6<5; i6++){
para(int i7=0; i7<5; i7++){
...
}
}
}
}
}
}
}
... mirándolo en Visual Studio, parece que siempre se ejecuta el mismo bucle y no cambia el valor de la variable i. Por ahora, no comprenderá esto en profundidad hasta que vea la llamada a la pila.
Si miramos la pila de llamadas, ahora se ve así...
MyRecursiveFunction()
MiFunciónRecursiva()
MiFunciónRecursiva()
MiFunciónRecursiva()
MiFunciónRecursiva()
MiFunciónRecursiva()
MiFunciónRecursiva()
...
entonces la conclusión del pensamiento inicial es que sin duda queremos ver alguna recursividad... pero ¿dónde? El código del ejemplo
myRoot.ChildNodes.Add(new SiteMapNode(this, i.ToString(), i.ToString(), i.ToString())
)
; new SiteMapNode() y myRoot.ChildNodes.Add(), esto ya no es tan misterioso si lo miramos con reflector.
Problemas de depuración
Finalmente :) Menos palabras, más acción eólica...
Dado que se vuelve a renderizar fácilmente, lo volveré a renderizar en mi máquina simplemente adjuntando windbg (Archivo/Adjuntar al proceso) a w3wp.exe y presionando g para comenzar. Luego, el problema reaparece y el programa se cancela diciéndome que se trata de un desbordamiento de pila (lo cual ya sabemos).
(7e4.ddc): Desbordamiento de pila: código c00000fd (primera oportunidad)
Las excepciones de primera oportunidad se informan antes de cualquier manejo de excepciones.
Esta excepción puede esperarse y manejarse.
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
Revisemos la pila y usemos el comando !clrstack para ver cómo se abortó, pero solo podemos ver....
0:016> !clrstack
ID de subproceso del sistema operativo: 0xddc (16)
ESP EIP 02163000 686b5cb4 System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode)
... Esto realmente no nos ayuda mucho. A veces, cuando nos encontramos con un desbordamiento de pila, algunos de estos problemas surgen usando el comando !clrstack. Por lo tanto, también necesitamos usar el comando !dumpstack para ver la pila sin formato.
0:016> !pila de volcado
ID de subproceso del sistema operativo: 0xddc (16)
Marco actual: (MethodDesc 0x68b03720 +0x4 System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode))
ChildEBP RetAddr llamador, destinatario de la llamada
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())
...
Bien, parece que el problema está en la propiedad ChildNodes. Cuando se utiliza esta propiedad, se llama a la función GetChildNodes, que vuelve a llamar a la función BuildSiteMap, que a su vez llama a la propiedad ChildNodes, y así sucesivamente, provocando un desbordamiento de pila.
en conclusión
En la documentación sobre BuildSitemap puedes encontrar el siguiente párrafo:
El método BuildSiteMap lo llama la implementación predeterminada de los métodos FindSiteMapNode, GetChildNodes y GetParentNode. Si anula el método BuildSiteMap en una clase derivada, asegúrese de que cargue los datos del mapa del sitio solo una vez y los devuelva en llamadas posteriores.
Para evitar la recursividad y el desbordamiento de la pila, es mejor evitar llamar a este método. Como en el ejemplo de BuildSiteMap, podemos usar el método AddNode para agregar nodos secundarios.
Esto está archivado en el artículo Proveedores de mapas del sitio, que también vale la pena leer.
BuildSiteMap generalmente no debe llamar a métodos o propiedades proporcionados por otros mapas del sitio, porque muchos métodos y propiedades implementarán llamadas a BuildSiteMap de forma predeterminada. Por ejemplo, RootNode en BuildSiteMap provoca recursividad, lo que provoca que finalice con un desbordamiento de pila.
http://www.cnblogs.com/Ring1981/archive/2006/10/19/443280.html