Şimdi arkadaşlar belleklerin yaşam döngüsü ve veri tipleri ile başlayalım.
Öncellikle stack ve heap'in ne olduğunu ve mimari farklarının ne olduğunu anlamamız gerekiyor. Aşağıda özet olarak tekrar vereceğim fakat burada javascriptten'de bağımsız olarak öğrenmekte fayda var.
1. Stack (Yığın)
- Yığın yani stack dediğimiz şeyi üst üste dizilmiş tabaklar gibi düşünebilirsiniz. Burada LIFO - Last in first out dediğimiz bir konsept var. Son giren, ilk çıkar demek. Siz üst üste dizilmiş tabaklarda her zaman en son koyduğunuzu yani en üsttekini alırsınız. Mantık bu.
Peki neden hızlı?:
Bölüm 1
Javascriptte ve diğer bir çok dilde de aynı şekilde bellek yönetimi arka planda 3 aşamalı bir döngü ile çalışır.
- Allocation yani "Yer Ayırma": Değişken tanımladığımız zaman JS Motoru bellekte yer ayırır.
- Use yani "Kullanım": Değişkeni okuduğumuz ve değiştirdiğimiz aşama.
- Release yani "Serbest Bırakma": Artık o veriye ihtiyaç kalmadığında belleğin temizlenmesi. (Garbage collection buraya giriyor ama ileride inceleyeceğiz bu durumu.)
Stack ve Heap kavramları:
1. Stack (Yığın) Bellek:
- Çok hızlıdır, düzenlidir fakat boyutu sınırlıdır.
- Primitive yani ilkel veri tiplerini saklar. (Number, String, Boolean, null, undefined, Symbol, BigInt)
- Buradaki veriler değer (value) olarak saklanır. Bir değişkeni diğerine eşitlediğinde, fotokopi çeker gibi değerin kopyasını oluşturur. Birini değiştirmek diğerini etkilemez.
2. Heap (Küme) Bellek:
- Büyük, dağınık bir depo aslında. Dinamik olarak büyüyebilir ama erişimi Stack'e göre bir tık daha yavaştır.
- Reference (Referans) tipleri saklar. (Yani nesneleri saklar: Object, Array, Function)
- Değişkenin kendisi (ismi) Stack'te durur ama değeri Heap'teki adresi yani referansı gösterir. Bir değişkeni diğerine eşitlediğinde evin anahtarını kopyalamış gibi olursun. İkisi de aynı evi açarlar.
(lab/1'in altıntaki kodları inceleyin. F12 -> Konsola gidin ve sonuçları da inceleyin.)
Bölüm 2
C veya C++ dillerde oluşturduğun bir veriyi işin bitince manuel olarak silmen gerekiyor. Silmezsen bilgisayarın RAM'i doluyor ve program çöküyor.
Javascriptte ise Garbage Collector (GC) adında bir mekanizma var. Arka planda çalışan bir temizleyici aslında bu. Sürekli belleği izliyor ve ihtiyaç olmayan verileri siliyor.
Pekii arkadaşlar ihtiyaç olup olmadığına nasıl karar veriyor?
Ana Kural: Reachability (Ulaşılabilirlik)
GC'nin tek ve çok basit bir kriteri var. Ben bu veriye kök dizinden (biz kök dizine root diyoruz.) ulaşabiliyor muyum?
-
Roots (Kökler): Global değişkenler (window gibi) veya şuan çalışan fonksiyonun yerel değişkenleri kök kabul ediliyor. Bunlar silinmezler.
-
Referanslar: Eğer bir kök, bir objeyi tutuyorsa (referans oluyorsa yani :)) o obje ulaşılabilir'dir ve silinmez.
-
Kopuk bağlar: Eğer bir objeye giden tüm yollar (referanslar) kesilirse, o obje ÇÖP (Garbage) olur.
Peki nasıl siliyor?
Algoritmanın adı: Mark-and-Sweep (İşaretle ve Süpür)
Modern tarayıcıların çoğu bu algoritmayı kullanıyorlar. En basit haliyle şöyle çalışıyor.
-
Mark (İşaretle): GC, köklerden başlıyor ve ulaşabildiği her objeyi canlı olarak işaretliyor.
-
Sweep (Süpür): İşaretlenmemiş olan her şeyi bellekten siler.
(lab/2'in altıntaki kodları inceleyin. F12 -> Konsola gidin ve sonuçları da inceleyin.)
Bölüm 3
Az önce sizinle garbage collector'ın nasıl çalıştığını öğrendik. Peki ya iki obje birbirini tutuyorsa ama dış dünyadan kimse onları tutmuyorsa ne olacak :)
- Circular Referance (Döngüsel Referans):
- Obje A, Obje B'yi tutuyor. Obje B'de Obje A'yı tutuyor.
- Çok eski tarayıcılarda garbage collector sadece "Buna kaç kişi bakıyor?" diye sayıyordu. İkisi birbirine baktığı için sayı asla 0 olmazdı ve bu objeler sonsuza kadar bellekte kalırdı. (Buna memory leak diyoruz, daha sonra geleceğiz)
- Günümüzdeki motorlar Mark-and-sweep mantığını kullandığı için artık bu bir sorun değil.
Mantığı çok basit: İki arkadaş el ele tutuşup uzay boşluğunda süzülüyor olsun. Birbirlerini tutuyorlar (referans var) ama gemiye (Root/Kök) bağlı değiller. Modern GC, ikisini de uzay çöpü olarak görür ve temizler.
- Memory Leak (Bellek Sızıntısı) Nedir?: Bellek sızıntısı, artık ihtiyaç duymadığın bir verinin, yanlışlıkla veya unutkanlıkla hala "İhtiyaç Var" gibi işaretli kalmasıdır. GC, "Bunu silersem program bozulabilir" korkusuyla o veriye dokunamaz.
Bu durum zamanla RAM'i şişirir, tarayıcıyı yavaşlatır ve en sonunda sayfayı çökertir ("Aw, Snap!" hatası buradan geliyor işte).
(lab/3'in altıntaki kodları inceleyin. F12 -> Konsola gidin ve sonuçları da inceleyin.)