Endereço original: http://blogs.msdn.com/tess/archive/2006/10/10/ASP.NET-Crash-_2D00_-Crazy-looping-in-a-SiteMap.aspx
Publicado: terça-feira, 10 de outubro de 2006, 16h10
Autor: Tess
Um dia, recebi um e-mail sobre meu blog fazendo as seguintes perguntas, brevemente descritas a seguir:
Eu queria criar um mapa do site rapidamente, então substituí o método BuildSiteMap() e dentro dele escrevi um loop que adicionou alguns nós falsos do mapa do site.
substituição pública SiteMapNode BuildSiteMap(){
para (int i = 0; i < 5; i++)
myRoot.ChildNodes.Add(novo SiteMapNode(isto, i.ToString(), i.ToString(), i.ToString()));
retornar meuRoot;
}
Ao executar o programa, ocorre um estouro de pilha e o servidor trava. Usei o depurador para percorrer e encontrei algo realmente estranho:
1) int i = 0
2) eu <5
3) minha raiz...
4) int eu = 0
5) eu < 5
etc.
O valor de i nunca parece aumentar, a menos que eu chame o SiteMapNode (acesse uma propriedade, chame um método), que parece que o loop está correto.
O que torna esse loop indeterminado? À primeira vista, pode ser um bug do compilador ou do CLR
(quando tive esse problema, eu realmente não sabia sobre navegação em sites no ASP.NET 2.0, mas encontrei estes artigos... http://weblogs. asp. net/scottgu/archive/2005/11/20/431019.aspx e http://aspnet.4guysfromrolla.com/articles/111605-1.aspx , as descrições são realmente boas.)
pensamentos iniciais
O mais importante sobre esse problema é que ele sempre reinicia, o que significa que pode ser depurado na hora. Mas não vamos tão longe ainda, vamos olhar para trás e ver o que temos agora...
1. Estouro de pilha
2. Um ciclo que começa de novo e de novo
Já discuti o estouro de pilha em uma postagem anterior do blog e vou repeti-lo... O estouro de pilha ocorre quando muitos ponteiros de função, ponteiros de variáveis e parâmetros são alocados, de modo que a quantidade de memória alocada na pilha não é suficiente. De longe, a causa mais comum de estouros de pilha é a recursão ininterrupta. Em outras palavras, a função A chama a função B, a função B chama a função A...
Então a pilha de chamadas se parece um pouco com isso ....
...
funçãoB()
funçãoA()
funçãoB()
funçãoA()
Ok, está tudo muito bem, mas isso apenas explica o Stack Overflow. Então, o que está acontecendo com esse ciclo maluco?
OK... imagine que existe tal função (há um ponto de interrupção em -->)
void MinhaFunçãoRecursiva(){
for(int i=0; i<5; i++){
--> MinhaFunçãoRecursiva();
}
}
Quando você parar pela primeira vez no ponto de interrupção, o valor de i deverá ser 0, e a pilha de chamadas ficará assim...
MinhaFunçãoRecursiva()
...
Agora chame a função MyRecrusive Cada vez que a função em si for chamada, i=0 aparecerá novamente (embora não estejamos realmente no mesmo loop). Se você chamar a função MyRecrusive várias vezes e substituí-la pelo código real executado, ela executará um código semelhante ao seguinte:
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++){
...
}
}
}
}
}
}
}
... olhando no visual studio, parece que o mesmo loop é sempre executado e não altera o valor da variável i. Por enquanto, você não terá um entendimento profundo disso até ver a chamada da pilha.
Se olharmos para a pilha de chamadas, ela agora se parece com isto...
MyRecursiveFunction()
MinhaFunçãoRecursiva()
MinhaFunçãoRecursiva()
MinhaFunçãoRecursiva()
MinhaFunçãoRecursiva()
MinhaFunçãoRecursiva()
MinhaFunçãoRecursiva()
...
então a conclusão do pensamento inicial é que sem dúvida queremos olhar para alguma recursão... mas onde? O código no exemplo
myRoot.ChildNodes.Add(new SiteMapNode(this, i.ToString(), i.ToString(), i.ToString())
)
; new SiteMapNode() e myRoot.ChildNodes.Add(), isso não é mais tão misterioso se olharmos com o refletor.
Problemas de depuração
Finalmente:) Menos palavras, mais ação windbg...
Como ele é renderizado novamente com facilidade, irei renderizá-lo novamente em minha máquina simplesmente anexando windbg (Arquivo / Anexar ao processo) ao w3wp.exe e pressionando g para iniciar. O problema reaparece e o programa é interrompido, informando que é um estouro de pilha (o que já sabemos).
(7e4.ddc): Estouro de pilha - código c00000fd (primeira chance)
As exceções de primeira chance são relatadas antes de qualquer tratamento de exceção.
Esta exceção pode ser esperada e tratada.
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
Vamos verificar a pilha e usar o comando !clrstack para ver como ela foi abortada, mas só podemos ver....
0:016> !clrstack
ID do thread do sistema operacional: 0xddc (16)
ESP EIP 02163000 686b5cb4 System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode)
... Isso realmente não nos ajuda muito. Às vezes, quando encontramos estouro de pilha, alguns desses problemas surgem ao usar o comando !clrstack. Portanto, também precisamos usar o comando !dumpstack para visualizar a pilha bruta.
0:016> !lixo
ID do thread do sistema operacional: 0xddc (16)
Quadro atual: (MethodDesc 0x68b03720 +0x4 System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode))
Chamador ChildEBP RetAddr, 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())
...
Ok, parece que o problema está na propriedade ChildNodes. Quando esta propriedade é utilizada, é chamada a função GetChildNodes, que chama novamente a função BuildSiteMap, que por sua vez chama a propriedade ChildNodes, e assim por diante, causando um estouro de pilha.
para concluir
Na documentação sobre BuildSitemap você pode encontrar o seguinte parágrafo:
O método BuildSiteMap é chamado pela implementação padrão dos métodos FindSiteMapNode, GetChildNodes e GetParentNode. Se você substituir o método BuildSiteMap em uma classe derivada, certifique-se de que ele carregue os dados do mapa do site apenas uma vez e os retorne nas chamadas subsequentes.
Para evitar recursão e estouro de pilha, é melhor evitar chamar este método. Como no exemplo BuildSiteMap, podemos usar o método AddNode para adicionar nós filhos.
Isso está arquivado no artigo Provedores de mapas de sites, que também vale a pena ler.
BuildSiteMap geralmente não deve chamar métodos ou propriedades fornecidas por outros mapas de site, porque muitos métodos e propriedades implementarão chamadas BuildSiteMap por padrão. Por exemplo, o RootNode no BuildSiteMap causa recursão, fazendo com que ele termine com um estouro de pilha.
http://www.cnblogs.com/Ring1981/archive/2006/10/19/443280.html