Alamat asli: http://blogs.msdn.com/tess/archive/2006/10/10/ASP.NET-Crash-_2D00_-Crazy-looping-in-a-SiteMap.aspx
Diterbitkan: Selasa, 10 Oktober 2006 16.10
Penulis: Tess
Suatu hari, saya menerima email tentang blog saya menanyakan pertanyaan-pertanyaan berikut, dijelaskan secara singkat sebagai berikut:
Saya ingin membuat peta situs dengan cepat, jadi saya mengganti metode BuildSiteMap(), dan di dalamnya saya menulis sebuah loop yang menambahkan beberapa node peta situs palsu.
penggantian publik SiteMapNode BuildSiteMap(){
untuk (int saya = 0; saya < 5; saya++)
myRoot.ChildNodes.Add(SiteMapNode baru(ini, i.ToString(), i.ToString(), i.ToString()));
kembalikan myRoot;
}
Saat menjalankan program, terjadi stack overflow dan server crash. Saya menggunakan debugger untuk menelusuri dan menemukan sesuatu yang sangat aneh:
1) int i = 0
2) saya <5
3) Root saya...
4) ke dalam saya = 0
5) saya <5
dll.
Nilai i sepertinya tidak pernah meningkat kecuali saya memanggil SiteMapNode (mengakses properti, memanggil metode), yang sepertinya loopnya benar.
Apa yang membuat putaran ini tidak dapat ditentukan? Sekilas mungkin itu adalah bug kompiler atau CLR
(Ketika saya mendapat masalah ini, saya benar-benar tidak tahu tentang navigasi situs di ASP.NET 2.0, tetapi saya menemukan artikel ini... http://weblogs.asp. net/scottgu/archive/2005/11/20/431019.aspx dan http://aspnet.4guysfromrolla.com/articles/111605-1.aspx , deskripsinya sangat bagus.)
pemikiran awal
Hal terpenting tentang masalah ini adalah masalah ini selalu dimulai ulang, yang berarti masalah ini dapat di-debug saat itu juga. Tapi jangan melangkah sejauh itu, mari kita melihat ke belakang dan melihat apa yang kita miliki sekarang...
1. Tumpukan meluap
2. Sebuah siklus yang dimulai berulang kali
Saya sudah membahas stack overflow di posting blog sebelumnya, dan saya akan mengulanginya... Stack overflow terjadi ketika terlalu banyak fungsi pointer, pointer variabel, dan parameter yang dialokasikan, sehingga jumlah memori yang dialokasikan pada stack tidak cukup. Sejauh ini, penyebab paling umum dari stack overflow adalah rekursi yang tidak dapat dihentikan. Dengan kata lain, fungsi A memanggil fungsi B, fungsi B memanggil fungsi A...
Jadi callstacknya terlihat seperti ini....
...
fungsiB()
fungsiA()
fungsiB()
fungsiA()
Oke, semuanya baik-baik saja, tapi itu hanya menjelaskan Stack Overflow. Jadi apa yang terjadi dengan siklus gila ini?
Oke...bayangkan ada fungsi seperti itu (ada breakpoint di -->)
batalkan Fungsi Rekursif Saya(){
untuk(int i=0; i<5; i++){
--> FungsiRekursif Saya();
}
}
Saat Anda pertama kali berhenti di breakpoint, nilai i harusnya 0, dan tumpukan panggilan akan terlihat seperti ini...
Fungsi Rekursif Saya()
...
Sekarang panggil fungsi MyRecrusive. Setiap kali fungsi itu sendiri dipanggil, i=0 akan muncul lagi (meskipun kita tidak berada dalam loop yang sama). Jika Anda memanggil fungsi MyRecrusive beberapa kali dan menggantinya dengan kode sebenarnya yang dijalankan, maka akan mengeksekusi kode yang mirip dengan berikut:
untuk(int i=0; i<5; i++){
untuk(int i2=0; i2<5; i2++){
untuk(int i3=0; i3<5; i3++){
untuk(int i4=0; i4<5; i4++){
untuk(int i5=0; i5<5; i5++){
untuk(int i6=0; i6<5; i6++){
untuk(int i7=0; i7<5; i7++){
...
}
}
}
}
}
}
}
...melihatnya di visual studio, sepertinya loop yang sama selalu dijalankan dan tidak mengubah nilai variabel i. Untuk saat ini, Anda tidak akan memiliki pemahaman mendalam tentang hal ini sampai Anda benar-benar melihat panggilan tumpukan.
Jika kita melihat pada callstack, callstack sekarang terlihat seperti ini...
MyRecursiveFunction()
Fungsi Rekursif Saya()
Fungsi Rekursif Saya()
Fungsi Rekursif Saya()
Fungsi Rekursif Saya()
Fungsi Rekursif Saya()
Fungsi Rekursif Saya()
...
jadi kesimpulan dari pemikiran awal adalah kita pasti ingin melihat beberapa rekursi...tapi di mana? Kode pada contoh
myRoot.ChildNodes.Add(new SiteMapNode(this, i.ToString(), i.ToString(), i.ToString())
)
;
new SiteMapNode() dan myRoot.ChildNodes.Add(), ini tidak lagi misterius jika kita melihatnya dengan reflektor.
Masalah debug
Akhirnya :) Lebih sedikit kata, lebih banyak tindakan...
Karena mudah dirender ulang, saya akan merender ulang di mesin saya hanya dengan melampirkan windbg (File / Lampirkan ke proses) ke w3wp.exe dan menekan g untuk memulai. Masalahnya kemudian muncul kembali dan program dibatalkan dan memberi tahu saya bahwa itu adalah stack overflow (yang sudah kita ketahui).
(7e4.ddc): Stack overflow - kode c00000fd (kesempatan pertama)
Pengecualian pada kesempatan pertama dilaporkan sebelum penanganan pengecualian apa pun.
Pengecualian ini mungkin diharapkan dan ditangani.
eax=0fa4235c ebx=02beca74 ecx=02beca74 edx=02becb54 esi=02becb54 edi=02beca74
eip=686b5cb4 esp=02163000 ebp=02163004 iopl=0 nv naik 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
Mari kita periksa tumpukannya dan gunakan perintah !clrstack untuk melihat bagaimana pembatalannya, tetapi kita hanya dapat melihat....
0:016> !clrstack
ID Utas OS: 0xddc (16)
ESP EIP 02163000 686b5cb4 System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode)
... Ini tidak banyak membantu kami. Terkadang ketika kita menghadapi stack overflow, beberapa masalah seperti itu muncul saat menggunakan perintah !clrstack. Oleh karena itu, kita juga perlu menggunakan perintah !dumpstack untuk melihat tumpukan mentah.
0:016> !tumpukan sampah
ID Utas OS: 0xddc (16)
Bingkai saat ini: (MethodDesc 0x68b03720 +0x4 System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode))
Penelepon RetAddr AnakEBP, Callee
02163004 686b1fc4 (MethodDesc 0x68aeff30 +0x18 Sistem.Web.SiteMapNode.get_ChildNodes())
0216300c 0f765641 (MetodeDesc 0xfa42328 +0x59 ViewSiteMapProvider.BuildSiteMap())
0216303c 686b5cdf (MethodDesc 0x68b03720 +0x2f System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode))
02163074 686b1fc4 (MethodDesc 0x68aeff30 +0x18 Sistem.Web.SiteMapNode.get_ChildNodes())
0216307c 0f765641 (MetodeDesc 0xfa42328 +0x59 ViewSiteMapProvider.BuildSiteMap())
021630ac 686b5cdf (MethodDesc 0x68b03720 +0x2f System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode))
021630e4 686b1fc4 (MetodeDesc 0x68aeff30 +0x18 Sistem.Web.SiteMapNode.get_ChildNodes())
021630ec 0f765641 (MetodeDesc 0xfa42328 +0x59 ViewSiteMapProvider.BuildSiteMap())
0216311c 686b5cdf (MethodDesc 0x68b03720 +0x2f System.Web.StaticSiteMapProvider.GetChildNodes(System.Web.SiteMapNode))
02163154 686b1fc4 (MethodDesc 0x68aeff30 +0x18 Sistem.Web.SiteMapNode.get_ChildNodes())
0216315c 0f765641 (MetodeDesc 0xfa42328 +0x59 ViewSiteMapProvider.BuildSiteMap())
...
Oke, sepertinya masalahnya ada pada properti ChildNodes. Ketika properti ini digunakan, fungsi GetChildNodes dipanggil, yang memanggil fungsi BuildSiteMap lagi, yang selanjutnya memanggil properti ChildNodes, dan seterusnya, menyebabkan stack overflow.
sebagai kesimpulan
Dalam dokumentasi tentang BuildSitemap Anda dapat menemukan paragraf berikut:
Metode BuildSiteMap dipanggil dengan implementasi default metode FindSiteMapNode, GetChildNodes, dan GetParentNode. Jika Anda mengganti metode BuildSiteMap di kelas turunan, pastikan metode tersebut memuat data peta situs hanya sekali dan mengembalikannya pada panggilan berikutnya.
Untuk menghindari rekursi dan stack overflow, sebaiknya hindari pemanggilan metode ini. Seperti pada contoh BuildSiteMap, kita dapat menggunakan metode AddNode untuk menambahkan node anak.
Ini diarsipkan dalam artikel Penyedia Peta Situs, yang juga layak dibaca.
BuildSiteMap secara umum tidak boleh memanggil metode atau properti yang disediakan oleh peta situs lain, karena banyak metode dan properti akan mengimplementasikan panggilan BuildSiteMap secara default. Misalnya, RootNode di BuildSiteMap menyebabkan rekursi, menyebabkannya berakhir dengan stack overflow.
http://www.cnblogs.com/Ring1981/archive/2006/10/19/443280.html