Değişkenler, Tipler, Stack ve Heap

Bir değişkenin bellekte neyi temsil ettiğini; değer, referans, stack, heap ve garbage collection kavramlarıyla öğrenin.

Bir nesneyi başka değişkene atadıktan sonra ilk nesnenin de değişmesi, yeni başlayanların en sık karşılaştığı şaşırtıcı durumlardan biridir. Sorun, değişkenin nesnenin kendisini mi yoksa ona erişen bir referansı mı tuttuğunu bilmemektir.

Değişken ve tip

Değişken, bir değere erişmek için verdiğimiz isimdir. Tip, değerin hangi işlemleri desteklediğini ve nasıl temsil edileceğini belirleyen sözleşmedir. Statik tipli diller birçok uyumsuzluğu derleme sırasında; dinamik tipli diller çalışma sırasında yakalar.

let first = { balance: 100 };
let second = first;
second.balance = 50;

first ve second iki ayrı nesne değildir; aynı nesnenin bellekteki konumuna ulaşan referansı paylaşır. Bu yüzden first.balance da 50 olur. { ...first } yalnızca ilk seviyedeki alanları kopyalayan shallow copy (yüzeysel kopya) üretir. İç içe bir address nesnesi varsa iki kopya yine aynı address referansını kullanabilir. Bütün iç yapıyı ayırmak gerektiğinde kontrollü deep copy veya immutable güncelleme kullanılmalıdır.

Stack ve heap

Call stack, aktif fonksiyon çağrılarını LIFO, yani “son giren ilk çıkar” düzeninde tutar. Fonksiyon çağrılınca parametreler, yerel değişkenler ve dönüş adresini içeren bir stack frame eklenir; fonksiyon bitince bu frame çıkarılır. Heap, yaşam süresi tek fonksiyon çağrısını aşabilen dinamik nesneler için kullanılan daha geniş bellek alanıdır.

Bu ayrım dile ve çalışma zamanına göre ayrıntıda değişir. “Primitive her zaman stack’tedir, nesne her zaman heap’tedir” ezberi güvenilir değildir; derleyici optimizasyon yapabilir. Önemli olan değerlerin sahipliği, paylaşımı ve yaşam süresidir.

Garbage collection ne yapar?

Garbage collector, artık erişilemeyen nesnelerin belleğini geri kazanır. Programcının her nesneyi elle serbest bırakma yükünü azaltır fakat bellek sızıntısını imkânsız yapmaz. Kullanılmayan bir nesneye hâlâ referans tutuluyorsa collector onu canlı kabul eder.

C ve C++ manuel bellek yönetimine daha fazla kontrol verir; Rust sahiplik ve borrow kurallarıyla birçok hatayı derleme sırasında engeller; Java, C# ve JavaScript yönetilen bellek kullanır. Seçim kontrol, güvenlik ve geliştirme maliyeti dengesidir.

Yaygın hatalar

  • Paylaşılan nesneyi fark etmeden değiştirmek
  • Sonsuz recursion ile call stack’i taşırmak
  • Event listener veya cache içinde gereksiz referans tutmak
  • Büyük nesneleri ihtiyaçtan uzun yaşatmak

Kendin dene

Bir nesneyi iki değişkene atayıp değişikliği gözlemleyin. Sonra shallow copy oluşturun ve iç içe bir alan ekleyerek kopyanın sınırını test edin.