---
title: 'Bellek Yaşam Döngüsü'
source: 'https://academia.sh/tr/kurslar/eszamansiz-javascript/bellek-yasam-dongusu'
course: 'Eşzamansız JavaScript ve Çalışma Zamanı'
language: tr
updated: '2026-08-17T18:09:44+00:00'
license: 'CC BY-SA 4.0'
---

# Bellek Yaşam Döngüsü

Ayırma, kullanım ve serbest bırakma aşamaları; erişilebilirliğin kök kümesi; eşzamansız kodda zamanlayıcı, dinleyici ve sonuçlanmamış sözlerin bellekte tuttukları.

Önceki konuda birkaç kez "bellekte kalır" denildi: yarışı kaybeden sözler, iptal edilmeyen
istekler, tüketilmeyen tamponlar. Neyin, neden ve ne kadar süreyle kaldığı ise
açıklanmadı.

Bu konu onu açıklar. Başlangıç noktası, her programlama dilinde ortak olan üç aşamalı
döngüdür; JavaScript'i ayıran, üçüncü aşamanın kime ait olduğudur.

## Üç Aşama

**Ayırma.** Bir değer için yer ayrılır. JavaScript'te bu, açıkça istenmez; bir nesne,
dizi, dizgi ya da fonksiyon yazıldığında kendiliğinden olur.

**Kullanım.** Ayrılan yer okunur ve yazılır. Programın asıl işi budur.

**Serbest bırakma.** Yer geri verilir. C gibi dillerde bunu program yapar; JavaScript'te
çalışma zamanı yapar.

Üçüncü aşamanın otomatik olması, sorunun ortadan kalktığı anlamına gelmez; sorunu
dönüştürür. Elle yönetilen bellekte hata "erken serbest bırakmak"tır — Bilgisayarlar Nasıl
Çalışır kursundaki sarkan işaretçi. Otomatik yönetimde hata "gereğinden uzun süre
başvurmak"tır. İlkinde program çöker, ikincisinde şişer.

Nesneler, aynı kursta tanıtılan bellek düzenindeki **öbek (heap)** alanında yaşar; yerel
değişkenler ise çağrı yığıtındaki çerçevede tutulur. Çerçeve, fonksiyon döndüğünde
kaybolur; öbekteki nesne ise ona başvuran kimse kalmadığında toplanabilir hale gelir.

## Erişilebilirlik ve Kökler

Çalışma zamanı, bir nesnenin "gerekli olup olmadığını" bilemez. Bunun yerine ölçülebilir
bir ölçüt kullanır: **erişilebilirlik (reachability)**. Bir kök kümesinden başlayıp
başvuruları izleyerek ulaşılabilen her nesne yaşar; ulaşılamayan her nesne toplanabilir.

Kök kümesi şunlardan oluşur:

- Yürüyen çağrı yığıtındaki yerel değişkenler ve parametreler
- Genel kapsamdaki bağlamalar
- Modül düzeyindeki bağlamalar
- **Bekleyen zamanlayıcıların geri çağrıları**
- **Kayıtlı olay dinleyicileri**
- **Sonuçlanmamış sözlerin geri çağrıları**

Son üçü bu kursun konusudur ve çoğu bellek sorununun kaynağıdır. Bir zamanlayıcı
kurulduğunda, geri çağrısı ve o geri çağrının kapattığı bütün değişkenler bir kök haline
gelir. Kapanış kavramı JavaScript'te Nesneler ve Fonksiyonlar kursunda tanımlanmıştı;
buradaki eklenti, kapanışın **yaşam süresini eşzamansız bir olayın belirlemesidir**.

## Bir Nesne Ne Zaman Toplanabilir

Kural, çalıştırılabilir bir örnekle gösterilebilir. Aşağıdaki program iki nesne üretir:
biri yalnız bir fonksiyonun içinde kullanılır, diğeri bir zamanlayıcı geri çağrısı
tarafından kapatılır.

Toplama anını gözlemek için iki araç kullanılır. `FinalizationRegistry`, kaydedilen bir
nesne toplandığında bir geri çağrı çalıştırır. Toplamayı elle tetiklemek için de çalışma
zamanı ilgili seçenekle başlatılır; aksi halde toplayıcı kendi zamanlamasına göre çalışır
ve çıktı belirlenimci olmaz.

```js
const kayitDefteri = new FinalizationRegistry((ad) => {
  console.log("toplandı:", ad);
});

function geciciOlcum() {
  const olcum = { istasyon: "A1", deger: 21.4 };
  kayitDefteri.register(olcum, "geçici ölçüm");
  return olcum.deger;
}

function zamanlayicininTuttugu() {
  const olcum = { istasyon: "B2", deger: 19.8 };
  kayitDefteri.register(olcum, "zamanlayıcının tuttuğu ölçüm");
  return setTimeout(() => console.log("zamanlayıcı çalıştı:", olcum.istasyon), 60);
}

console.log("geçici ölçümün değeri:", geciciOlcum());
const kimlik = zamanlayicininTuttugu();

global.gc();
await new Promise((karar) => setTimeout(karar, 10));
global.gc();
console.log("ilk toplama turu bitti");

clearTimeout(kimlik);
global.gc();
await new Promise((karar) => setTimeout(karar, 10));
global.gc();
await new Promise((karar) => setTimeout(karar, 10));
console.log("zamanlayıcı iptal edildikten sonraki tur bitti");
```

```
$ node --expose-gc bellek.mjs
geçici ölçümün değeri: 21.4
toplandı: geçici ölçüm
ilk toplama turu bitti
toplandı: zamanlayıcının tuttuğu ölçüm
zamanlayıcı iptal edildikten sonraki tur bitti
```

Sonuç iki kuralı birden gösterir. Birinci nesne, fonksiyon döndüğü anda erişilemez hale
geldi ve ilk toplama turunda toplandı — döndürülen yalnız bir sayıydı, nesnenin kendisi
değil. İkinci nesne, zamanlayıcı beklediği sürece toplanamadı; ancak `clearTimeout` ile
kök ortadan kalktıktan sonra toplandı.

Buradaki `--expose-gc` seçeneği yalnız gözlem içindir. Uygulama kodunda toplayıcıyı elle
çağırmak ne gereklidir ne de doğrudur; kararı çalışma zamanı verir.

## Eşzamansız Kökler ve Ölçüm Akışı

Aynı kural, kursun örneğindeki her yapıya uygulanır.

**Kendi kendini zamanlayan akış.** Zamanlayıcılar konusundaki `akisBasla` işlevi, her
turda yeni bir zamanlayıcı kurar. Akış durdurulmadığı sürece geri çağrı ve kapattığı
sayaçlar, tampon, istasyon listesi bellekte kalır. Akışı durduran işlev aynı zamanda bir
bellek işlevidir.

**İptal edilmeyen istek.** Yarış tabanlı zaman aşımında kaybeden söz sonuçlanmaz;
sonuçlanmayan sözün geri çağrıları da kök olduğu için, isteğin tuttuğu tampon ve yanıt
gövdesi serbest bırakılamaz.

**Kaldırılmayan dinleyici.** İptal işareti dersinde dinleyici `once` seçeneğiyle
kaydedilmişti. Seçenek olmasaydı dinleyici işaret nesnesine bağlı kalır, o da geri
çağrının kapattığı her şeyi tutardı.

Üç örneğin ortak dersi şudur: **eşzamansız bir işi başlatmak, aynı zamanda bir kök
oluşturmaktır.** İşi sonlandıran ya da iptal eden kod, o kökü kaldıran koddur.

## Kapanış Neyi Tutar

Bir kapanışın kendi kapsamındaki *her* değeri tuttuğu sanılır. Çalışma zamanları böyle
davranmak zorunda değildir: yalnız iç fonksiyonun gerçekten başvurduğu bağlamalar
saklanır, diğerleri sıradan yerel değişken gibi ele alınır.

```js
const toplananlar = [];
const kayitDefteri = new FinalizationRegistry((ad) => {
  toplananlar.push(ad);
});

function kapanisKur() {
  const hamTampon = { ad: "ham ölçüm dizisi" };
  const ozet = { istasyon: "A1", deger: 21.4 };
  kayitDefteri.register(hamTampon, "ham tampon");
  kayitDefteri.register(ozet, "özet");

  return function ozetiVer() {
    return ozet.deger;
  };
}

const ozetiVer = kapanisKur();

global.gc();
await new Promise((karar) => setTimeout(karar, 10));
global.gc();
await new Promise((karar) => setTimeout(karar, 10));

console.log("kapanışın verdiği değer:", ozetiVer());
console.log("toplananlar:", toplananlar.sort().join(", ") || "(yok)");
console.log("ölçüm alanları:", Object.keys(process.memoryUsage()).sort().join(", "));
```

```
$ node --expose-gc kapanis.mjs
kapanışın verdiği değer: 21.4
toplananlar: ham tampon
ölçüm alanları: arrayBuffers, external, heapTotal, heapUsed, rss
```

İki nesne de aynı fonksiyonun yerelleriydi; dönen kapanış yalnız birine başvuruyordu.
Başvurulmayan tampon toplandı, başvurulan özet kapanış yaşadığı sürece kaldı.

Buna karşın bu davranış bir güvence değildir; kapanışın hangi bağlamaları sakladığı çalışma
zamanının kararıdır. Taşınabilir kural şudur: **uzun yaşayacak bir kapanışta yalnız
gerekli olanı kapatın.** Büyük bir yapıdan tek bir alan gerekiyorsa, kapanışı yapının
tamamı yerine o alanla kurmak hem açık hem güvenlidir.

Son satır, bellek kullanımının hangi eksenlerde raporlandığını gösterir. Değerleri
makineye ve yüke göre değiştiği için burada yalnız alan adları yazdırıldı: `rss` sürecin
işletim sisteminden aldığı toplam bellek, `heapTotal` ayrılmış öbek boyutu, `heapUsed`
öbekte gerçekten kullanılan bölüm, `external` ile `arrayBuffers` ise JavaScript öbeği
dışında tutulan ikili veridir. Sızıntı takibinde izlenen alan genellikle `heapUsed`
değeridir.

## Serbest Bırakma Ne Zaman Görünür

Bir nesnenin erişilemez hale gelmesi ile belleğin gerçekten geri verilmesi aynı an değildir.
Toplayıcı kendi zamanlamasına göre çalışır; ölçülen bellek kullanımı erişilemezlikten
hemen sonra düşmeyebilir.

Bunun iki pratik sonucu vardır. Birincisi, "bellek düşmedi" gözlemi tek başına sızıntı
kanıtı değildir; ölçüm birden çok toplama turu boyunca tekrarlanmalıdır. İkincisi,
`FinalizationRegistry` geri çağrısının ne zaman — hatta çalışıp çalışmayacağı — güvence
altında değildir; program sonlanırken hiç çalışmayabilir. Bu nedenle kaynak temizliği
oraya yazılmaz; temizlik açık bir kapatma çağrısıyla ya da `finally` bloğuyla yapılır.

## Özet

- Bellek yaşam döngüsü ayırma, kullanım ve serbest bırakma aşamalarından oluşur;
  JavaScript'te üçüncüsünü çalışma zamanı yapar.
- Otomatik yönetim sarkan işaretçi hatasını ortadan kaldırır, yerine "gereğinden uzun
  başvurma" hatasını koyar.
- Bir nesne, kök kümesinden erişilemez hale geldiğinde toplanabilir; kökler arasında
  bekleyen zamanlayıcılar, kayıtlı dinleyiciler ve sonuçlanmamış sözler de vardır.
- Eşzamansız bir iş başlatmak bir kök oluşturur; iptal ya da sonlandırma o kökü kaldırır.
- Kapanış yalnız başvurduğu bağlamaları saklayabilir; uzun yaşayan kapanışlarda gereksiz
  değer kapatmamak taşınabilir kuraldır.
- Erişilemezlik ile belleğin geri verilmesi aynı an değildir; sonlandırıcı geri çağrılar
  temizlik için kullanılamaz.

## Sonraki Adım

Bu derste toplayıcı bir kara kutu olarak kullanıldı: "erişilemeyeni toplar" denildi ve
elle tetiklendi. Sonraki ders kutuyu açar. Erişilebilirliğin nasıl hesaplandığı, başvuru
sayma yönteminin çevrimlerde neden başarısız olduğu, kuşak varsayımının toplayıcıyı nasıl
hızlandırdığı ve zayıf başvuru yapılarının bu düzenekte hangi boşluğu doldurduğu ele
alınacak.
