Yunus Emre Alpak

Stack ve Heap: Bir Programın Belleği Nasıl Çalışır?

2026 · 05 · 119 dk okuma

Stack ve heap, her programın görmezden geldiği iki oda — ta ki bir çökme, bir GC duraklaması ya da bir performans sorunu kapıyı çalana kadar. Bu yazıda ikisini önce dilden bağımsız açıyoruz, sonra Swift ve Dart'a bağlıyoruz; tahsis maliyetinden value/reference dengesine kadar.

Çalışan bir programın bellek haritası: stack, heap, data ve kod bölgeleri

Her programın görmezden geldiği iki oda

Hangi dilde yazarsan yaz, programın çalışırken belleğini iki temelde farklı bölgeye ayırır: stack (yığın) ve heap (öbek). Çoğu zaman bunları düşünmezsin — ta ki performans, beklenmedik bir çökme ya da çöp toplayıcının (garbage collector) davranışı seni düşünmeye zorlayana kadar. Oysa bu ikisini bir kez gerçekten anlamak çok şeyi açıklar: neden bazı tahsisler (allocation) "bedava"yken bazıları GC'yi tetikler, neden derin bir özyineleme (recursion) çöker, neden value/reference type ayrımı önemlidir. Konu dilden bağımsız; aynı RAM, aynı kurallar. Anlatırken Swift ve Dart'a da bağlayacağız.

Önce harita: bir program belleği nasıl bölüyor

Bir program çalışmaya başladığında işletim sistemi ona bir adres uzayı verir ve bunu bölgelere ayırır (yüksek adresten düşüğe):

Kritik nokta: stack de heap de aynı fiziksel RAM'dedir. Aralarındaki fark donanım değil, yönetim biçimidir.

Stack: hızlı, otomatik, disiplinli

Stack, fonksiyon çağrılarının çalışma alanıdır. Bir fonksiyon çağrıldığında stack'e bir çerçeve (stack frame) eklenir (push): local değişkenler, "iş bitince nereye döneceğim" bilgisi (return address) ve bir önceki çerçeveye bağ. Fonksiyon dönünce bu çerçeve kaldırılır (pop) — ki bu çoğu zaman fiziksel silme değil, sadece stack pointer'ı geri kaydırmaktır. Yapı LIFO'dur (last-in-first-out — son giren ilk çıkar).

Özellikleri:

Bir çökmede gördüğün stack trace, aslında bu stack'in o andaki fotoğrafıdır: çerçevelerin "beni kim çağırdı" zinciri.

Heap: büyük, esnek, ama bedeli var

Heap, boyutunu ya da ömrünü derleme anında bilemediğin veya kendisini yaratan fonksiyondan daha uzun yaşaması gereken veriler içindir.

Özellikleri:

Stack vs Heap, tek bakışta

Stack ve heap karşılaştırması: tahsis, ömür, hız, sahiplik ve temizlik

StackHeap
Tahsisişaretçiyi kaydır (tek işlem)tahsisçiden iste (pahalı)
Ömürfonksiyon dönünce biter (otomatik)sen/GC bırakana kadar
Hızçok hızlı (cache-dostu)daha yavaş (dağınık)
Boyutküçük (~MB)büyük
Sahiplikthread'e özelpaylaşılır

Kısaca: stack control flow'u (akışı) taşır; heap veriyi saklar.

Diller bunu nasıl kullanıyor: değer mi, referans mı?

Swift ve Dart'ta bellek yerleşimi: struct inline, class ve referans heap'te, Dart'ta Smi inline

Bir dilin sana verdiği asıl kaldıraç şu: veriyi inline (içine gömülü, çoğunlukla stack'te) mi tutabiliyorsun, yoksa yalnızca bir referans (pointer) arkasında (heap'te) mi?

Swift ikisini de verir. struct bir value type'tır: verisi kopyalanarak inline tutulur; küçük struct'lar heap tahsisi olmadan stack'te ya da başka bir nesnenin içine gömülü yaşayabilir. class ise reference type'tır: nesne heap'te yaşar, ona giden küçük bir referans stack'te/register'da durur. Heap nesnelerini ARC (otomatik referans sayımı) yönetir — deterministiktir ama her retain/release atomik bir işlemdir. Ufak bir tuzak: Array, String, Dictionary value type'tır ama altında heap buffer + referans sayımı kullanır (copy-on-write).

struct Point { var x, y: Int }    // value     → inline
class  Node  { var next: Node? }  // reference  → heap

let p = Point(x: 1, y: 2)          // verisi doğrudan burada (stack)
let n = Node()                     // nesne heap'te, referansı stack'te

Dart'ta struct yoktur — her sınıf reference type'tır. Yani senin nesnelerinin gövdesi heap'te, referansları stack'te yaşar.

class Point { int x, y; Point(this.x, this.y); }

var p = Point(1, 2);  // nesne heap'te, referansı stack'te
var n = 42;           // küçük int → inline (Smi), heap'e gitmez

"Her şey heap'te" yanılgısı

Buradan yanlış bir zihinsel modele kaymamak için iki şeyi netleştirelim.

Birincisi, "her şey object/reference" ≠ "her şey heap'te." Dart küçük tam sayıları (Smi — small integer) işaretçi etiketleme (pointer tagging) ile doğrudan işaretçinin içinde tutar; heap'e hiç gitmez. Optimize kodda double'lar register'da (unboxed) tutulabilir, hatta escape analizi bazı tahsisleri tamamen eleyebilir.

İkincisi ve en önemlisi: call stack her dilde, value/reference farkından bağımsız olarak kullanılır. Dart'ta bile fonksiyon çağrıları, dönüş adresleri ve referansların kendisi stack'te yaşar. Derin bir widget ağacının build() özyinelemesi tamamen call stack üzerinde koşar (ve stack overflow verebilir). Reference type, nesnenin gövdesini heap'e taşır; stack'i ortadan kaldırmaz. Yani "Dart sadece heap kullanır" yanlıştır: nesnelerinin verisi heap'e yaslanır (çünkü struct yok), ama akış, referanslar ve küçük değerler stack'i tüm hızıyla kullanır.

Peki bu performansı belirler mi?

Burada abartmamak lazım. Value type'lar, sıcak yollarda (hot path) heap tahsisini ve GC baskısını azaltır ve sana kontrol verir — yoğun döngüler, parçacık sistemleri, ses/sinyal işleme gibi yerlerde gerçek bir avantaj. Ama "her zaman daha hızlı" değildir: büyük struct'lar pahalıya kopyalanır; copy-on-write gizli bir heap tahsisi + atomik referans sayımı saklar; ARC'nin atomik retain/release'i bedavaya gelmez; Dart'ın generational GC'si ise kısa ömürlü nesneleri çok ucuza tahsis eder (genç kuşakta neredeyse pointer-bump). Üstelik gerçek uygulamalarda performansı çoğu zaman dilin stack/heap eğilimi değil, mimari, I/O ve render disiplini belirler. "Daha fazla kontrol" ile "her zaman daha hızlı" aynı şey değildir.

Zihinsel model

Tek cümleye sığdırırsak: stack, çağrılar için hızlı, otomatik, thread'e özel bir çalışma alanıdır (akış + local'ler + referanslar); heap ise uzun ömürlü ve dinamik veriler için büyük, paylaşılan, yönetilen bir depodur. Diller, veriyi ne kadar inline (value) tutabildiğin ile referans arkasında (heap) tutmak zorunda olduğun konusunda ayrışır — Swift seçim sunar (struct/class), Dart referansa yaslanır. Ama ikisi de her iki bölgeyi kullanır; stack hiçbir zaman ortadan kalkmaz. Hangisinin ne olduğunu bilmek; tahsis maliyetini, çökmeleri, GC'yi ve value-vs-reference dengelerini tek bir modelde açıklar.