---
title: 'Bellek Yönetimi'
source: 'https://academia.sh/tr/kurslar/ruby-uretim/bellek-yonetimi'
course: 'Ruby Ekosistemi ve Üretim'
language: tr
updated: '2026-10-21T05:03:44+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir bloğun ayırdığı nesne sayısını yineleme başına ölçer'
    - 'Dize, sembol ve donmuş dize arasındaki ayırma farkını sayılarla gösterir'
    - 'Çöp toplayıcının sayaçlarını ve nesne boyutunu standart kütüphaneyle okur'
    - 'İlk koşumun ısınma payını ölçümün kendisinin bir girdisi olarak açıklar'
---

# Bellek Yönetimi

Aynı iş aynı kodla çalışırken bir bloğun kaç nesne ayırdığı da bir ölçüm sorusudur. Ders nesne ayırma sayılarını, çöp toplayıcının görünür sayaçlarını ve ilk koşumun neden sonrakilerden farklı olduğunu ölçer.

Önceki ders işçi sayısını ortamın belirlediğini gösterdi. Her işçinin kendi belleği olduğuna göre bir işçinin belleğinin neyle dolduğu sorusu sırada. Bu soru da kursun ölçü eksenine bağlanır: **bu sonucu ne belirliyor?** Bir bloğun ayırdığı nesne sayısı, makinenin ya da zamanın değil, bloğun kendisinin işidir. Bu dersin gözlemi `ayrilan nesne` satırıdır ve belirleyeni **kod**dur.

M11/K06'da bir bedelin hangi kaynağa (derleme · koşum · bellek · bekleme) **taşındığı** sorulmuştu. Burada ayrılan nesne sayısının **ne belirlediği** sorulur; bedel ölçülür ama sınıf belirleyendir. Çöp toplayıcının nesneleri nasıl yaşlandırıp topladığı ise M08/K04'te görülmüştü. Bu ders o mekanizmanın içine inmez; yalnız dışarıdan okunabilen sayıları, yani **nesne ayırma (allocation)** sayılarını ölçer. Çekirdeğin `belirleyen.rb` dosyası ve bu dersin kesiti `bellek.rb` aşağıda olduğu gibi verilir; ikisi de kaynaktır, çıktısı yoktur. Çekirdek bir modeldir ve bu dersin ölçümünde modelin yalnız kuralı kullanılır: ayrılan nesne sayısı çalışma ortamının değil bloğun işidir.

```ruby
# belirleyen.rb — bir kosum modeli ve bir sonucu neyin belirledigini siniflandiran yardimci

# Kosum: sonucun bagli olabilecegi uc girdi. Bu uc girdinin disinda kalan her sey koddur.
Kosum = Data.define(:kilit, :tohum, :ortam)

TABAN = Kosum.new(
  kilit: { "bicimleyici" => "1.4.2", "ayristirici" => "2.0.1" },
  tohum: 1,
  ortam: { saat: Time.utc(2026, 10, 16, 9, 0), cekirdek: 4, degiskenler: {} }
)

# Her degisken tek bir girdiyi degistirir; otekiler tabandaki gibi kalir.
DEGISKENLER = {
  bagimlilik: TABAN.with(kilit: { "bicimleyici" => "1.5.0", "ayristirici" => "2.1.0" }),
  sira: TABAN.with(tohum: 2),
  ortam: TABAN.with(ortam: { saat: Time.utc(2027, 3, 1, 9, 0), cekirdek: 8,
                             degiskenler: { "ISCI_SAYISI" => "3" } })
}

# Kural: hesap once tabanda, sonra her degiskende bir kez kosar. Sonucu degistiren ilk
# degisken (bagimlilik, sira, ortam sirasiyla) belirleyendir; hicbiri degistirmiyorsa kod.
def belirleyen(hesap)
  taban = hesap.call(TABAN)
  degisti = DEGISKENLER.to_h { |ad, kosum| [ad, hesap.call(kosum) != taban] }
  tur = degisti.key(true)&.to_s || "kod"
  kanit = degisti.map { |ad, d| "#{ad[0].upcase}#{d ? '+' : '-'}" }.join(" ")
  [tur, kanit, taban]
end
```

```ruby
# bellek.rb — blok uc kez kosar; son kosumda ayrilan nesneler sayilir
# Bellek: blok uc kez kosar; ilk kosumlar isinmadir, son kosumda ayrilan nesneler sayilir.
def ayrilan_nesne
  sonuc = nil
  3.times do
    once = GC.stat(:total_allocated_objects)
    yield
    sonuc = GC.stat(:total_allocated_objects) - once
  end
  sonuc
end
```

## Aynı Binlik Dizi, Farklı Nesne Sayısı

`ayrilan_nesne` bir bloğu üç kez koşturur ve sonuncusunda `GC.stat(:total_allocated_objects)` sayacının farkını alır. Sayaç, o ana kadar ayrılan nesnelerin toplamıdır ve çöp toplayıcı bir nesneyi topladığında azalmaz; bu yüzden fark, bloğun yarattığı nesne sayısına eşittir. Aşağıdaki blok aynı bin elemanlı diziyi farklı yollarla kurar.

```ruby
# olcum.rb — ayni dizi, farkli nesne sayisi
require_relative "bellek"

olcumler = {
  "i.to_s" => -> { Array.new(1000) { |i| i.to_s } },
  "i" => -> { Array.new(1000) { |i| i } },
  ":sabit" => -> { Array.new(1000) { :sabit } },
  "\"sabit\"" => -> { Array.new(1000) { "sabit" } },
  "\"sabit\".freeze" => -> { Array.new(1000) { "sabit".freeze } },
  "-\"sabit\"" => -> { Array.new(1000) { -"sabit" } },
  "map" => -> { (1..1000).map { _1 * 2 } },
  "each_slice" => -> { (1..1000).each_slice(10).to_a },
  "<<" => -> { s = +""; 1000.times { s << "a" }; s },
  "+=" => -> { s = ""; 1000.times { s += "a" }; s }
}
olcumler.each { |ad, blok| puts "#{ad}: #{ayrilan_nesne(&blok)}" }
```

```
i.to_s: 1001
i: 1
:sabit: 1
"sabit": 1001
"sabit".freeze: 1
-"sabit": 1
map: 2
each_slice: 107
<<: 1002
+=: 2001
```

Her satırın okuması aynı mantığa dayanır. Tamsayı ve sembol nesne ayırmaz; dizinin kendisi tek nesnedir, bu yüzden `i` ve `:sabit` bir verir. `i.to_s` ve `"sabit"` her yinelemede yeni bir dize yaratır, bu yüzden bin bir verir. Donmuş dize değişmez ve aynı nesne yeniden kullanılır: `"sabit".freeze` ve `-"sabit"` bir verir. `map` dizi ve sonuç dizisi için iki, `each_slice` ise yüz parçalık ara dizilerle yüz yedi nesne ayırır. Son iki satır dize birleştirmeyi gösterir: `<<` aynı dizeye ekler, `+=` her adımda yeni bir dize kurar ve sayı yaklaşık iki katına çıkar.

Bu satırların hiçbiri süre ölçmez. Süre makineye, ayrılan nesne sayısı bloğa bağlıdır; bu dersin ölçüsü ikincisidir. **RB9.** Bu sayılar Ruby 4.0.7'de ölçüldü; dizi ve dize uygulamaları sürümler arasında değişirse sayılar da değişir.

Aynı blokları belirleyen kuralına verelim. Seçilen blok, bin elemanlı bir tamsayı dizisidir.

```ruby
# kural.rb — ayrilan nesne sayisini ne belirliyor
require_relative "belirleyen"
require_relative "bellek"

p belirleyen(->(_k) { ayrilan_nesne { Array.new(1000) { |i| i } } })
```

```
["kod", "B- S- O-", 1]
```

Sonuç birdir ve kanıt `B- S- O-`dur: kilit dosyasındaki sürümler, tohum ve ortam değişince sayı değişmedi. Belirleyen **kod**dur. Bloğun içinde `i.to_s` yerine `i` yazmak sayıyı bin birden bire indirir; bunu başka hiçbir girdi yapmaz. **RB10.** Kural yalnız çekirdeğin üç girdisini oynatır; bellek ayırmayı etkileyebilecek başka bir değişken, örneğin çöp toplayıcının ayarları, burada modelde yoktur.

## Çöp Toplayıcının Görünür Sayaçları

Ayrılan nesneler çöp toplayıcının işidir; bir nesne ulaşılamaz hâle gelince toplanır, ulaşılabilir kaldıkça yaşar. Aşağıdaki blok bin dizeyi bir diziye koyar, çöp toplayıcıyı zorla çalıştırır ve dizelerin yaşadığını sayar. Sonra çöp toplayıcının sayaçlarında beklenen anahtarların var olduğuna ve bir nesnenin boyutunun pozitif olduğuna bakar. Sayaçların değeri basılmaz: her koşumda ve her makinede farklıdır.

```ruby
# gc.rb — cop toplayici sayaclari ve nesne boyutu
require "objspace"

tutulan = Array.new(1000) { |i| i.to_s }
GC.start
p ObjectSpace.each_object(String).count >= 1000
p %i[major_gc_count minor_gc_count heap_live_slots].all? { |anahtar| GC.stat.key?(anahtar) }
p ObjectSpace.memsize_of("a" * 1000).positive?
p tutulan.size
```

```
true
true
true
1000
```

Üç mantıksal sonuç ve bir sayı basıldı. Dizeler `GC.start` çağrısından sonra da yaşıyor, çünkü `tutulan` dizisi onlara ulaşıyor. Sayaçların adları var; değerlerine bakmak yerine varlıklarını sınadık. `ObjectSpace.memsize_of` bir nesnenin bellekte kapladığı bayt sayısını verir ve bin karakterlik dize için pozitiftir. **RB11.** Değerlerin kendisi, bayt sayısı dahil, ölçümde basılmadı; çünkü değişir ve değişen değer bu kursun taşıdığı sayıların güvenini zayıflatır.

## Sınırlayıcı Ölçüm

`ayrilan_nesne` bloğu neden üç kez koşturur? Çünkü ilk koşum sonrakilerden farklı olabilir. İlk çağrıda yorumlayıcının iç önbellekleri henüz dolmamış olabilir ve bu hazırlık için ayrılan nesneler sonraki koşumlarda tekrarlanmaz. Nedenini burada ölçmüyoruz; yalnız farkın yönünü ölçüyoruz. Aşağıdaki blok aynı bloğu üç kez koşturur ve ilk sayıyı sonuncuyla karşılaştırır.

```ruby
# isinma.rb — ilk kosum isinmadir, olcumun kendisi bir girdidir
sayilar = Array.new(3) do
  once = GC.stat(:total_allocated_objects)
  Array.new(1000) { |i| i.to_s }
  GC.stat(:total_allocated_objects) - once
end
p sayilar.first > sayilar.last
```

```
true
```

İlk koşum sonuncudan fazla nesne ayırdı. Aynı blok, aynı kod, aynı girdiler; fark ölçümün kendisinden geliyor. Bu yüzden `ayrilan_nesne` ilk iki koşumu ısınma sayar ve yalnız sonuncunun farkını döndürür. Tek koşumluk bir ölçüm bu ısınma payını da taşırdı ve sayı bloğun gerçek işinden büyük çıkardı. Sonuç: ölçümün nasıl yapıldığı, sonucu belirleyen girdilerden biri gibi davranır; bir ölçümü paylaşırken ısınmanın nasıl ele alındığı da paylaşılmalıdır.

Bu, kuralın sınırını da gösterir. `belirleyen` üç girdiyi oynatır; ölçüm yönteminin kendisini oynatmaz. Isınma payı kodun değil, ölçümün bir özelliğidir ve üç değişkenin hiçbirine bağlı değildir. Bu yüzden `ayrilan_nesne` içinde saklıdır, kuralın dışında. **RB12.** Isınmanın büyüklüğü burada yalnız yönüyle ölçüldü; bir ya da iki ek koşumun yeterli olup olmadığı bu ölçümden çıkmaz.

## Özet

- Bir bloğun ayırdığı nesne sayısını **kod** belirler: kural üç değişkende de aynı sayıyı gördü.
- Tamsayı ve sembol nesne ayırmaz; donmuş dize aynı nesneyi yeniden kullanır; `i.to_s` ve `"sabit"` her yinelemede yeni bir dize yaratır.
- `+=` dize birleştirmede `<<`'in yaklaşık iki katı nesne ayırır.
- Çöp toplayıcının sayaçları `GC.stat` ile, nesne boyutu `ObjectSpace.memsize_of` ile okunur; değerler basılmaz, varlıkları ve işaretleri sınanır.
- İlk koşum ısınma payı taşır; ölçüm yönteminin kendisi sonucu etkileyen bir girdidir.

## Sonraki Adım

Bir bloğun kaç nesne ayırdığı biliniyor; bir programın hangi yönteminin sıcak yol olduğu henüz bilinmiyor. Bir sonraki ders çağrı sayılarını sayar ve yavaş olanın çağrı sayısından çıkarılamayacağını gösterir.
