Recursive Fonksiyonlar Stackoverflow ve sonrası

Recursive Function Stackoverflow exception Handling

0 3.694

Her birini farklı bir yazı ile ele alabileceğimiz konuları başlangıç tadında işleyip tümevarım da bir problemi ortaya koyacağımız bu makalede Recursive fonksiyonların kullanımı Overflow ve StackOverflow oluşumu ve bu hataları nasıl kontrol altına alabileceğimizi işleyeceğiz. Makale başlığı bana iyi, kötü çirkin’i hatırlatıyor. iyi  ‘Recursive’  Kötü  ‘StackOverflow’  çirkin ise her şey olabilir. Öncelikle makalemizin kahramanlarından biraz bahsedelim.

Recursive Fonksiyonlar (rekürsif

Temel olarak birbirini çağıran ve sistematik bir döngü oluşturan yapılar için kullanılan fonksiyonlar Recursive (Yenilemeli, tekrarlayan) fonksiyonlardır. En kült örnekleri faktöriyel ve fibonacci sayı dizisidir. Biraz daha bizim dünyamızın örnekleriyle ele alacak olursak Kategori , alt kategori veya N-Tree listelemelerin işlendiği kod parçalarında çokça kullanılır.

Recursive ( Rekürsif )Fonksiyon Senaryosu

Kategorilerimizin listelendiği bir senaryo kurgulayalım. Category tablosu , SubCategory tablosu olarak iki tablo ile başlayabilirdik fakat bizim senaryomuzun gerçekleşmesi için tablonun recursive olması gerekiyor. Tek tablo ile bu işi çözeriz. Tablomuzun ismi Categories olarak değişti ve SubCategory tablosundaki tüm kayıtlar Categories tablonda ParentID ile ifade edilir hale geldi. Aşağıda kategorilerimizi recursive olarak listeleyen örneği inceleyebilirsiniz. Bu kod bloğu ile N adet  kategori ve alt kategoriyi işleyebiliriz.

Data

 

 

result değişkenin incelediğimizde elimizde Parent Child ilişkisi sağlanmış objelerin hazır olduğunu görebiliriz. Tekrar geri dönmek üzere makalenin ilk bölümünü burada bırakalım ve ikinci bölüme geçelim.

Overflow Hatası

Öncelikle Overflow kavramına bakalım, bu kavram sözlük anlamı olarak taşma , sığmama, kapasitesinden fazla yüklenme gibi terimlerle ifade edilir.Aşağıdaki kod parçasını çalıştırdığımızda OverflowException fırlattığını görebiliriz.  Overflow hatası System sınıfı altında System.OverflowException olarak tanımlıdır ve standart bir hata yakalama mekanizması ( Try – Catch ) bu hatayı yakalayabilir.

 

StackOverflow Hatası

Bahsedilen Overflow olayının Stack tarafından bildirilmesidir.

Nasıl Oluşur ?

The Matrix filminden unutulmaz bir söz ; “Başlangıcı olan her şeyin birde sonu vardır ”  Stack’de sonsuz değil, o halde taşma durumu gayet olasıdır.  StackOverFlow oluşumunda Infinite Loop olarak adlandırılan sonsuz döngüler baş roldedir. Birde işin içinde recursive fonksiyon varsa çok daha dikkatli olmak gerekir. Daha önce recursive fonksiyonu kendi kendini çağıran fonksiyonlar olarak adlandırmıştık.

Sistemde bir metodun belirli bir süre recursive olarak kalması ve sürekli kendi kendini çağırması ( DeepCall yapması ) derleyici optimizasyonu tarafından normal olmayan bir durum olarak algılanır ve rekürsif kuyruğu gittikçe artar, artan recursive kuyruk (tail recursive) oluşumu yığın taşmasına yol açar.

Stackoverflow oluşumunu detaylı olarak ilgili referansları takip ederek inceleyebilirsiniz.

Localloc
Ret

Şimdi recursive örneğimiz üzerinden biraz değişiklik yapıp sonucu gözlemleyelim.

  • Recursive metot sayacı için _deepCallCount  adında bir değişken tanımladım.
  • DBCategories listesine infinite loop oluşabilmesi için iki adet Category nesnesi ekledim.
  • Son olarak GetChilds metodunda recursive çağrı olan durumlarda sayacın arttırılması için if bloğu ekledim.

 

Kodu bir kaç kez çalıştırıp _deepCallCount ‘u kontrol ettiğimde 7953 – 7960 arası çağrımdan sonra stackoverflow aldığımı gözlemledim.

 

StackOverFlow Exception

Çıktıya bakacak olursak bir şeylerin ters gittiğini anlayabiliriz , hata riskine karşın kodumuzu Try – Catch bloğunda  yazmıştık. Fakat doğru aksiyonu alamadık, ekranda “Hata” uyarımızı göremiyoruz..

Try – Catch Bloğu ?

Stackoverflow’u açıklarken özellikle Stack tarafından bildirilmesidir şeklinde bir yorum yapmıştım. Bunun nedeni aslında StackOverFlow‘un standart hata ayıklama ile ayrıştırılamıyor olmasıydı. Örneğin Try-Catch bloğu bu hatayı yakalayamaz. Bunun nedeni ise C# derleyicisinin CLR (Common Language Runtime) üzerinden çalışmasıdır. Stackoverflow hatası ise Localloc üzerinden IL (intermediate language) tarafından fırlatılır. Bu sebeple try – catch bloğunda hatayı ayrıştıramayız.

Senaryomuzun İyi ( ne kadar iyi olduğu da tartışılır ) ve Kötü kahramanlarını inceledik şimdi sıra çirkin’de
Kapsamlı bir projede IIS W3WP’nin düştüğünü , debug ve test ortamlarında herhangi bir hata ile karşılaşılmadığı halde Production ortamında sürekli hata aldığınızı varsayalım. Stackoverflow hatasından süpheleniyorsunuz ve kısıtlı zamanda çözüme ulaşmanız gerekiyor. Gerçekten çirkin bir durum ( Hata bağımsız olarak sizde benzer durumda kaldıysanız tecrübelerinizi bu yazının atlına yorum olarak ekleyebilirsiniz 🙂 )

Hatayı Kontrol Etme ve Problem Tespitini kolaylaştırma ?

Adım adım neler yapabileceğimizden bahsedelim.

  • Öncelikle işinizin bir yerinde IIS varsa belirli aralıklarda IIS dump almakta fayda var, Dump üzerinden bir şeyler çıkacaktır.
  • Stackoverflow’dan şüpheleniyorsak aklımıza recursive fonksiyonlar geldi, tek tek oturup recursive  mi arayacağız ? Üzerine bir de production süreci eklenecek. Bunun yerine kodlarımızda kendi kendini çağıran metotları bulan bir kod parçacığıyla bu işi hızlandıracağız
    ( Detect Recursive calls in C# code ) üzerinden kodlara ulaşabilirsiniz. Bu yöntem ile recursive fonksiyonlar adreslenecektir. Burada çok önemli bir nokta var , dll üzerinden bu metotları bulmak istiyorsak kodları barındıran class’ların Build Action özelliği kesinlikle Compile olarak işaretlenmeli. Aksi halde recursive metotları assambly’de göremeyeceğiz.
  • Yukarıda eklediğimiz _deepCallCount değişkenini hatırladınız mı ?  İşte bu durum hatayı kontrol etme yöntemlerinden biriydi. Belirli bir çağırımdan sonra StackOverflow Exception gönderebiliriz. Hatayı biz fırlattığımız için try – catch’de hatayı yakalayıp yönetebiliriz.
  • Diğer bir yöntem ise recursive fonksiyonların ayrı bir iş parçacığında işlenip belirlenen stack boyutuyla ele alınması Thread Constructors üzerinden detaylandırabilirsiniz.
  • Bizim senaryomuzdaki recursive örneği Id ParentId üzerinden ilerliyordu. Hatayı alabilmek için Id ve ParentId değeri aynı olan bir alt kategori eklemiştik.  O halde Database ayağı da bizim için önemli. Database üzerinde Where Id = ParentId olan kayıt var mı kontrol edilip düzeltilmelidir. Ayrıca tablolara bu şekilde kayıt eklenmesi veya güncellenmesi engellenmelidir.

Umarım faydalı bir yazı olmuştur.
Yeni problemlerde görüşmek üzere

Recursive StackOverflow Try-Catch

StackOverFlowException

 

Email adresiniz yayınlanmayacaktır.