C# Nesne Tabanlı Programlama Sınavı Programlama Temelleri Sınavı

C# Değer ve Referans Tipi Bellek Yönetimi

Nesne Yönelimli Programlama Dillerinde baş aktör olan nesneyi (object) gerçek dünya metaforları ile izah edebiliriz. Bu gayet anlamlı olabilir. Fakat daha iyi kavramak,  işin iç yüzünü, daha derinlerde çözümlemekle ancak mümkün olabilir. Derin sorumuz, “Bir program çalışma zamanında (runtime)  bellek yönetimini (memory management) nasıl gerçekleştirir.

Nesne yönelimli dillerle ilk tanışmam esnasında “new” anahtar kelimesini nerelerde ve neden kulanıldığını anlamak oldukça uzun zamanımı almıştı. Neden new anahtar kelimesi kullanıyoruz?Yapıcı metodların (Constructor) geri dönüş tipi neden bulunmaz? Çalışma zamanında durduk yere aldığımız ve canımızı çok sıkan “NullReferenceException” başlıklı hatalar neden alırız? İşte bütün bu soruların cevaplarını yazdığımız kodlarda, verilerin saklandığı yer olan RAM’de arayacağız.

Aynı zamanda Değer Tipi (Value Type) , Referans Tipi (Reference Type) kavramları da burada tanışacaklarımız arasında olacak.

Programlama dilleri uygulama yürütme süreci esnasında verilerin adresleneceği RAM bellek bölgesini birkaç farklı bölgeye ayırarak kullanırlar. Uygulama çalışma zamanında (runtime) açılacak olan değişken ve nesneleri bu bölgelerde oluşturarak, sahip oldukları verileri adreslerindeki yerlerine yerleştirirler. Öncelikli olarak bu bellek bölgelerini kısaca tanıyalım;

RAM Bellek bölgeleri Nelerdir?

I. Stack Bölgesi

Genel anlamda kullanılan dahili RAM ifadesidir. Stack bögelerine  mikroişlemcide(CPU)’de bulunan “Stack Pointer (SP)” vasıtası ile ile doğrudan erişilebilir. SP o anda çalışılan bellek bölgesinin adresini tutar. Tahsis işlemi için önceden ayrılacak bölgenin büyüklüğünü bilmesi gerekir. Buna göre arttırım veya azaltım yaparak veriyi uygun adreste bulur. Bilmesi gereken bu büyüklükler, .NET platformunun altyapısını oluşturan JIT derleyiciler tarafından sabit değerler ile tayin edilir. Örneğin int tipi 32 bitlik olup 4 byte büyüklüğündedir. Bir diğer örnek long tipi 64 bit yani 8 byte, boolen tipi ise 1 bit için 1 byte yer tutar. Program yüklendiğinde okunulan değişken tanımına dair kodlarınızı bu sabit değerler eşliğinde hesaplayarak SP’nin pozisyonlarını doğru konumlandırması için bellek tahsisatını yapar. Yapısal programlama dillerinde (C, Pascal vs…) edinilen tecrübeler, verilerin hepsini stack bölgesinde tutmanın esnekliği azalttığını göstermiştir.

II. Heap Bölgesi

Stack’de olduğu gibi Heap bölgesi RAM’de bulunan hafıza alanıdır. Bütün C# nesneleri Heap bölgesinde oluşurlar.  Stack’den farkı heap bölgesinde tahsisatı yapılacak nesnenin derleyici tarafından bilinmesine gerek duymaz. Heap bölgesinin kullanılması büyük esneklik kazandırır. Heap bölgesinden yer tahsisatı için “new” anahtarı kullanılır. Çalışma zamanında dinamik
olarak yaratılır. Derleme zamanında yapılmazlar.

Dezavantajı Stack bölgesine göre daha fazla işlem ayağı oluşması neticesi performans düşüklüğüdür. Ancak Nesne Yönelimli Programlama’nın temel ayağıdır.

III. Register Bölgesi

Stack ve Heap tahsisat mekanizmalarına göre çok hızlıdır. Sebebi mikroişlemcinin ikincil bellek bölgesinde bulunmasıdır. Mikroişlemcinin kaydedici (register) adını verdiğimiz bu bellek bölgelesine doğrudan erişim hakkımız yoktur. Tamamen altyapıda çalışan JIT (Just in Time) derleyicilerinin kontrolündedir.

IV. Static Bölge

Bellekteki herhangi bir bölgeyi temsil eder. Static alanlarda saklanan veriler programın bütün çalışma sürecinde ayakta kalırlar. Anahtar “static sözcüğüdür.

V. Sabit Bölge

Sabit(constant) değerler genellikle program kodlarının içine gömülü şekildedir. Değerleri asla değişmezler. Sadece okuma amaçlıdır.

VI. RAM Olmayan Bölge

Bellek bölgesini temsil etmeyen disk alanlarıdır. Kalıcı olması istenen verilerin saklandığı bölgedir.

Özellikle Stack ve Heap mantıksal alanları dahili RAM hafızada değişken (variable) ve nesnelerimiz için çok daha önemli konumlardadır.

Bu iki farklı mantıksal alanın birbirlerinden ayrılma nedeni ise değer tipi değişkenlerin ve referans tipi nesnelerin bu bellek bölgelerini farklı şekilde kullanmalarıdır. Farklılıklarını anlamak için Değer Tipi (Value Type) ve  Referans Tipi (Reference Type) kavramlarını örneklerle inceleyelim.

Değer Tipleri 

Yukarıdaki basit örneğimizde int tipinde “a” adında ki değişkenimiz, sonraki satırda 5 değerine sahip oluyor.  Bu durum  “a” değişkenini direkt olarak RAM’in Stack adı verilen bölgesinde derleyicilerin direktifi ile 4 byte büyüklüğünde bir bloğu adreslenmesini sağlar. İçeriğine uygun değeri ise bu adreslenen bellek bloğunda saklar. Değişkendeki değere erişim doğrudan adreslenen bölgeye başvurularak yapılır.

Program kodunun ilerleyen satırlarında x veya i değişkenine okuma yönlü başvuruda bulunduğunuzda Stack bellek bölgesindeki adresinden alınır ve kullanılır.

Eğer bu değişkenlere yazma yönlü bir durum meydana getirilirse sahip olacakları yeni değerler adreslenen yerlerine yazılır. Dolaysıyla önceki değerlerini kaybedeceklerdir.

Diğer bir senaryomuzda ise değişkenlerin birbirlerine eşitlenmesi durumu diğer bir adıyla kopyalanmaları sürecini ele alalım. Bir değişkeni başka bir değişkene eşitlendiğinizde yalnızca sahip olduğu değer o an kopyalanır. Sonrasında değişkenlere atayacağınız yeni değerler yalnızca yine kendisini etkileyecektir.

Görüldüğü üzere değişkenlerin değer tipi olarak nitelendirilmesinin nedeni tüm işlemlerin adreslenen RAM hücresine doğrudan erişim yapılması ve sahip oldukları değerlere direkt uygulanmasından kaynaklanmaktadır.

Aşağıda saydıklarımız değer tipi davranışında bulunan türlerdir ;

  • Değişken türleri:  “int”, “long”, “float”, “double”, “decimal”, “char”, “bool”, “byte”, “short”
  • Yapılar : “struct”
  • Sayılabilir Tipler : “enum”

Referans Tipleri

Referans Tipleri Stack ve Heap bellek alanlarını birlikte kullanılır. Söz konusu nesnenin sahip olduğu değerler Heap bellek alanında korunur. Bu ayrılmış bellek alanının başlangıç adres bilgisini (işaretçisi) ise Stack bellek alanında tutar. Nesneneye dair her türlü erişim işte bu işaretçi(pointer) üzerinden yapılır. Kısa bir örnekle inceleyelim;

Urun tipi, class ifadesinde geçen özelliklere sahip bir sınıftır. “u1” nesnesi Urun tipinden tanımlanmıştır. “u1” değişkeni belleğin Stack bellek bölgesinde oluşur. Bu anda nesne “null” durumdadır. “new” anahtar kelimesi ile Heap bellek alanında nesnenin büyüklüğü kadar bir alan tahsistı yapılır. Bu ayrılmış alanın işaretçi adres bilgisi (referans) “u1” nesnesinde saklanır. Nesnemiz artık null durumda değil, Heap’ta saklanan değerlere ulaşılabilen refransa sahiptir.

Bir nesne null durumda iken ona erişim yapmaya (okuma-yazma) kalkarsanız, “NullReferenceException” hatasını alırsınız.

Referans tiplerini değer tiplerinden ayıran en önemli senaryo, Aynı türde iki nesne birbirlerine eşitlendiğinde görülür. Örneğin ;

Örneğimizde u1 ve u2 Urun tipinde farklı özelliklere sahip iki nesnedir. Ancak “u2=u1” şeklinde bir eşitleme ile aslında eşitlenen Stack tarafındaki nesnenin referansları olacaktır. Kısaca u1’in referansı u2’ye kopyalanacaktır. Bu durumda referansları eşitlenen nesneler dolaysıyla aynı Heap bellek alanındaki değerlere işaret olacaktır. Bu durumda her iki nesne birbirine gerçekten eşit hale gelecektir. Bu noktadan sonra u1 veya u2 nesnelerinin özelliklerinde yapılacak her türlü okuma yazma işlemlerinde her zaman birbirlerini etkileyeceklerdir.

  •  “string”, “object”, “class”, “interface”, “array”, “delegate”, “pointer” referans tipi davranışında bulunan türlerdir.

Yorum

  • Bu gönderiye yorum yapılmaması ilk bakışta beni şaşırttı ama çok geçmeden anladım. Çok güzel bir paylaşım olmuş. Biz toplum olarak biraz aceleci olduğumuz için hemen “Merhaba Dünya” ile kodlamaya başlıyoruz. İşini mantığını da öğrenmek lazım. Paylaşımınız için teşekkürler.

      • Bu yazdığınız konu hakkında kafama takılan bir kaç soru var:

        1) Program içerisinde static olarak tanımladığımız değişkenler static bölgede mi tutuluyor?

        2) Program içerisinde tanımladığımız (ör. const int a = 20) sabiti belleğin sabit bölgesinde mi tutuluyor?

        • her iki sorunuzun cevabı için doğru tespitler yapmışsınız. const türevi tipler sabit alanda, static anahtar kelimeli değişkenler static bölgede bulunmaktadır. Tabi ki bu bellek bölgelerinin tahsisatı tamamen işletim sisteminin sorumluluğunda gerçekleştirilir. Nesne yönelimli programlama paradigmasında öğrenilmiş tasarım kalıplarında static değişkenler özeĺlikleri bakımından oldukça işlevseldir. Örneğin “singleton design pattern” incelemenizi öneririm…

Emre için bir cevap yazın X