Toplanabilir Dinamik Assembly’ler

   Hatırlayacak olursanız bir süre önce size çalışma-zamanı nasıl dinamik kod oluşturabileceğinizden bahsetmiştim. Geliştirdiğimiz örnek kod üzerinden ekrana “Merhaba Dinamik Dünya” yazan bir konsol uygulaması oluşturarak diske kaydetmiş, ardından da uygulamamız üzerinden oluşan kodu nasıl kullanabileceğimizi görmüştük. Makalemde basit bir uygulama ile örneklemeye çalıştığım reflection  emit çok güçlü; ama dikkatli kullanılmadığında da bir o kadar tehlikeli bir yöntemdir. Tehlikelidir diyorum; çünkü bilinçsiz kullanıldığında uygulamanızın önemli bir hafıza alanını işgal etmesine neden olacaktır.

    Ne demek istediğimi daha rahat anlatabilmek adına önceki makalemde paylaşmış olduğum kodu biraz düzenleyerek aşağıda yeniden sizlerle paylaşıyorum;

using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;

namespace Com.Enterprisecoding.CollectibleAssemblies {
    class Program {
        static void Main(string[] args) {
            var merhabaDunyaTuru = TuruOlustur();

            merhabaDunyaTuru.GetMethod("Main").Invoke(null, new string[] { null });
            merhabaDunyaTuru = null;

            YukluAssemblyleriYazdir();

            GC.Collect();

            YukluAssemblyleriYazdir();

            Console.ReadKey();
        }

        private static void YukluAssemblyleriYazdir() {
            Console.WriteLine("");
            Console.WriteLine("======================");
            Console.WriteLine("Yüklü assembly’ler");
            Console.WriteLine("======================");
            Console.WriteLine(string.Join(Environment.NewLine, AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name)));
        }

        private static Type TuruOlustur() {
            var assemblyAdi = new AssemblyName("Com.Enterprisecoding.MerhabaDinamikDunya");
            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyAdi, AssemblyBuilderAccess.Run);
            var modul = assemblyBuilder.DefineDynamicModule("MerhabaDinamikDunya");
            var typeBuilder = modul.DefineType("DinamikSinif", TypeAttributes.Public | TypeAttributes.Class);
            var metodbuilder = typeBuilder.DefineMethod("Main",
                MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.Public,
                typeof(void),
                new Type[] { typeof(string[]) });
            var ilOlusturucu = metodbuilder.GetILGenerator();
            ilOlusturucu.EmitWriteLine("Merhaba Dinamik Dünya");
            ilOlusturucu.Emit(OpCodes.Ret);

            return typeBuilder.CreateType();
        }
    }
}

   Dikkat edecek olursanız önceki makalemden farklı olarak bu sefer oluşan kodu dosya sistemine saklamak yerine çalışma zamanında kullanmayı tercih ettim. Dinamik oluşan assembly’i dosya sistemine saklamayacağım için de AssemblyBuilderAccess.RunAndSave demek yerine AssemblyBuilderAccess.Run diyerek hafıza kullanımı için hazırlıyorum. Ardından oluşan kodu çalıştırıp uygulamam tarafından hafızaya yüklenmiş tüm assembly’leri listeliyorum. Dikkat edecek olursanız işim bitince merhabaDunyaTuru değişkenimi sıfırlıyorum. Kullanılmayan tüm hafıza alanlarının temizlendiğinden emin olmak için tüm işlemlerin sonunda Garbage Collector’ü GC.Collect() yardımıyla tetikleyip tekrardan yüklü assembly’leri listeliyorum.

   Bu haliyle uygulamayı komut satırından çalıştırdığımızda aşağıdaki çıkti önümüze gelecektir;

AssemblyBuilderAccess.Run kullanıldığında oluşturulan türler her daim hafızada yüklü olarak kalacaktır

   Gördüğünüz gibi dinamik oluşturduğum tür artık uygulamamın hiç bir noktasında referans gösterilmese de hafızada kalmaya devam etmekte. Bu basit örnekten yola çıkarak oluşturulan kompleks uygulamalarda belirli iş mantıkları çerçevesinde dinamik olarak kodlar ürettiğimizi farz edelim. Bu dinamik kodlarla işimiz bittiğinde sıfırlayarak bu türlerden kurtulduğumuzu düşünsekte, yukarıdaki örnekte de görüldüğü gibi bu türler dinamik bile olsa, bir kez oluşturulduktan sonra her daim hafızada yüklü olarak kalacaktır. Dolayısıyla da uzun soluklu çalışan dinamik uygulamalarda bir süre sonrasında gereksiz bir hafıza yükü oluşması kaçınılmaz olacaktır.

   Bu sıkıntıyı çözmek adına alternatif yöntemler geliştirilebilir. Bu yöntemlerde ilk aklıma gelen dinamik kod çalıştırılmasının oluşturulan ikincil bir appdomain içerisinde gerçekleştirilmesidir. Oluşturulan bu ikincil appdomain’in işi bitince unload edilerek hafızada oluşturduğu yükten kurtulmak mümkün; fakat böylesi bir kod hem daha karmaşık hem de çalışma zamanında daha maliyetli olacaktır.

   Bu sıkıntıyı gören .Net Framework geliştiricileri 4.0 sürümü ile birlikte Dinamik assembly oluşturulurken kullanılan AssemblyBuilderAccess enum’una RunAndCollect değerini de eklemişlerdir. Bu parametre ile oluşturulan dinamik assembly’ler kullanılmaları sonrasında herhangi bir referans kalmaması durumunda hafızadan temizleneceklerdir. Örnekleme gerekirse; yukarıdaki kodumuzda dinamik assembly oluşturulurken kullandığımız AssemblyBuilderAccess.Run değeri yerine AssemblyBuilderAccess.RunAndCollect kullandığımızda aşağıdaki çıktı elde edilecektir;

AssemblyBuilderAccess.RunAndCollect kullanıldığında oluşturulan türlere bir referans kalmaması durumunda hafızadan temizleneceklerdir

   Görüldüğü gibi ilk kullanımdan farklı olarak RunAndCollect parametresi ile oluşturulan dinamik assembly’ler kullanımları sonunda bir referans kalmaması durumunda hafızdan atılmakta. Yukarıda, garbage collector devreye girdikten sonra yüklü assemb ly’ler ikinci defa listelendiğinde dinamik oluşturduğumuz Com.Enterprisecoding.MerhabaDinamikDunya assembly’si artık bulunmamakta.

   Hayatımızı kolaylaştıran Toplanabilir assembly’lerle çalışmanın tabi ki bir kaç kısıtı bulunmakta;

  • Öncelikle oluşturulan dinamik assembly’ler disk’e saklanamamaktadır
  • COM arayüzleri tanımlanamaz; fakat COM arayüzler kullanılabilir
  • DllImportAttribute özniteliği bulunan metod tanımlaması yapılamaz. Platform invoke yapılabilmesi, unmanaged kod çağrısı için toplanabilir olmayan assemby’ler kullanılabilir. Toplanabilir assembly’lerden bu türlerin tanımlı olduğu toplanabilir olmayan assembly’lerde yer alan türler kullanılabilir
  • Toplanabilir assembly’lerin yüklenmesinin tek yolu relection emit yöntemidir. Farklı bir yöntemle yüklenen hiç bir assembly daha sonradan hafızadan kaldırılamaz
  • Thread-static  ve Context-static değişkenler desteklenmemektedir

  Toplanabilir bir assembly’nin yaşam süresi bu assembly ve içerisindeki türlere olan referanslar ile belirlenmektedir. Bu assembly içerisindeki herhangi bir türe ait hiç bir referans kalmaması durumunda assembly hafızadan temizlenecektir. Burada dikkat edilmesi gereken nokta assembly içerisindeki türlere ait Type ve TypeBuilder referanslarıda sıfırlanmış olmalıdır. Benzer şekilde oluşturulan ModuleBuilder, TypeBuilder, AssemblyBuilder, LocalBuilder, ILGenerator gibi referanslarda assembly’nin temizlenmesini engelleyecektir.

   Yukarıda sizinlen paylaştığım örnek kodda Tür oluşturma işi aynı bir metod içerisinde yapıldığından metoddan geri dönüldüğünde metod içerisinde oluşturulmuş olan assemblyBuilder, modul, typeBuilder, metodbuilder ve ilOlusturucu gibi değişkenler temizlenecektir. Dolayısıyla toparlanabilir dinamik assembly’mize olan tek referans Main metodumuz içerisindeki merhabaDunyaTuru değişkeni olacaktır. Bu değişkenin de sıfırlanması sonrasında ise herhangi bir referans kalmayacağından bir sonraki döngüde tür Gerbage Collector tarafından temizlenecektir.

Fatih Boy

Ankara'da yaşayan Fatih, bir kamu kurumunda danışman olarak çalışmaktadır. ALM süreçleri, kurumsal veri yolu sistemleri, kurumsal altyapı ve yazılım geliştirme konularında destek vermektedir. Boş zamanlarında açık kaynak kodlu projeler geliştirmeyi ve bilgisini yazdığı makalelerle paylaşmayı seven Fatih, aynı zamanda Visual C# ve Visual Studio teknolojileri konusundan Microsoft tarafından altı yıl üst üste MVP (En Değerli Profesyonel) ödülüne layık görülmüştür. İş hayatı boyunca masaüstü uygulamaları, web teknolojileri, akıllı istemciler gibi konularda Asp.Net, Php, C#, Java programlama dilleri ve MySql, MsSql ve Oracle gibi veritabanı yönetim yazılımları ile çalışmıştır. İngilizce ve Türkçe olarak yayınlanan makalelerini gerek İngilizce bloğunda, gerekse de Türkçe bloğunda bulabileceğiniz gibi web sitesinden de açık kaynak kodlu geliştirdiği yazılımlarına ulaşabilirsiniz. vCard - Twitter - Facebook - Google+

3 yorum

  1. dhmm   •  

    Hocam c# icinde MOV v.s gibi assembly komutlarini runtime nasil calistirabiliriz.. ?

    Bilirsizni c++ ta asm { … } …
    Bunu benzeri veya baska bir yolu var mi c# ta ?

    • Fatih Boy   •     Yazar

      Merhaba dhmm,
      Bu makalemin konusu olmaması nedeniyle bu sorunu burada yanıtlamam doğru olmayacaktır. İletişim (Contact) bölümünden bana ulaşırsan yardımcı olmaya çalışayım.

  2. İlhan SAĞLIK   •  

    Emeğinize sağlık hocam çok güzel bir anlatım olmuş

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir